1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-05-12 12:00:13 +00:00
QB64-PE/source/utilities/const_eval.bas
2024-04-25 21:21:39 -07:00

1135 lines
42 KiB
QBasic

TYPE ParserState
index AS LONG
strIndex AS LONG
num AS ParseNum
errStr AS STRING
result AS STRING
END TYPE
'Steve Subs/Functins for _MATH support with CONST
FUNCTION Evaluate_Expression$ (e$, num AS ParseNum)
t$ = e$ 'So we preserve our original data, we parse a temp copy of it
PreParse t$
IF CONST_EVAL_DEBUG THEN _Echo "t$: " + t$
IF LEFT$(t$, 5) = "ERROR" THEN Evaluate_Expression$ = t$: EXIT FUNCTION
'Deal with brackets first
exp$ = "(" + sp + t$ + sp + ")" 'Starting and finishing brackets for our parse routine.
IF CONST_EVAL_DEBUG THEN _Echo "exp$: " + exp$
DIM Eval_E AS LONG, c AS LONG
DO
FindInnerParens exp$, C, Eval_E
IF Eval_E > 0 THEN
IF c = 0 THEN Evaluate_Expression$ = "ERROR - BAD () Count": EXIT FUNCTION
eval$ = getelements$(exp$, c + 1, Eval_E - 1)
ParseExpression2 eval$
eval$ = LTRIM$(RTRIM$(eval$))
IF LEFT$(eval$, 5) = "ERROR" THEN Evaluate_Expression$ = eval$: EXIT FUNCTION
' Check if element preceding the parens is a known function name
' If so, evaluate it now using the argument list we have
funcOp& = IsFunctionIdentifier(getelement$(exp$, c - 1))
IF funcOp& > 0 THEN
eval$ = EvaluateFunction$(funcOp&, eval$)
IF LEFT$(eval$, 5) = "ERROR" THEN Evaluate_Expression$ = eval$: EXIT FUNCTION
c = c - 1
END IF
IF CONST_EVAL_DEBUG THEN _Echo "eval$: " + eval$
leftele$ = getelements$(exp$, 1, c - 1)
rightele$ = getelements$(exp$, Eval_E + 1, numelements(exp$))
exp$ = leftele$
IF exp$ <> "" THEN exp$ = exp$ + sp
exp$ = exp$ + eval$
IF rightele$ <> "" THEN exp$ = exp$ + sp + rightele$
END IF
LOOP UNTIL Eval_E = 0
IF CONST_EVAL_DEBUG THEN _Echo "resulting exp$: " + exp$ + ", numelements: " + str$(numelements(exp$))
IF numelements(exp$) <> 1 THEN
Evaluate_Expression$ = "ERROR - Invalid characters in expression": EXIT FUNCTION
END IF
IF elementIsString&(exp$) THEN
num.typ = STRINGTYPE
num.s = exp$
ELSE
num.typ = elementGetNumericValue&(exp$, num.f, num.i, num.ui)
END IF
Evaluate_Expression$ = exp$
END FUNCTION
' Finds an innermost set of parens, returns the element indexes of the parens
'
' Gives 0 as startParen if there are no matching parens
SUB FindInnerParens (exp$, startParen AS LONG, endParen AS LONG)
startParen = 0
endParen = 0
strIndex = 0
paren = 0
DO
ele$ = getnextelement$(exp$, paren, strIndex)
IF paren = -1 THEN EXIT SUB
IF ele$ = ")" THEN endParen = paren: EXIT DO
IF paren > 1000 THEN EXIT SUB
LOOP
strIndex = 0
paren = 0
DO
ele$ = getprevelement$(exp$, paren, strIndex)
' Skip parens until we reach the ")" we found in the previous search
IF paren > endParen THEN _CONTINUE
IF paren = -1 THEN EXIT SUB
IF ele$ = "(" THEN startParen = paren: EXIT DO
IF paren > 1000 THEN EXIT SUB
LOOP
END SUB
' Grammar (excludes parens)
'
' comma_expression := expression ',' expression
' | expression
'
' expression := imp_logical
' | str_add
'
' str_add := primary_str '+' primary_str
' | primary_str
'
' imp_logical := eqv_logical 'IMP' eqv_logical
' | eqv_logical
'
' eqv_logical := xor_logical 'EQV' xor_logical
' | xor_logical
'
' xor_logical := or_logical 'XOR' or_logical
' | or_logical
'
' or_logical := and_logical 'OR' and_logical
' | and_logical
'
' and_logical := not_logical 'AND' not_logical
' | not_logical
'
' not_logical := 'NOT' relation
' | relation
'
' relation := term '<>' term
' | term '><' term
' | term '<=' term
' | term '>=' term
' | term '=<' term
' | term '=>' term
' | term '>' term
' | term '<' term
' | term '=' term
' | term
'
' term := mod '+' mod | mod '-' mod | mod
'
' mod := int_div 'MOD' int_div | int_div
'
' int_div := factor '\' factor | factor
'
' factor := unary '*' unary
' | unary '/' unary
' | unary
'
' unary := '-' exponent | exponent
'
' ' Note: NOT is a special case here similar to -, but it is handled in
' ' PreParse via parenthesis insertion
' exponent := numeric '^' unary
' | numeric 'ROOT' unary
' | numeric
'
' numeric := NUMBER | CONST_VAR
'
' string := STRING | CONST_VAR
FUNCTION CommaExpression&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "CommaExpression"
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
DIM tmpIndex AS LONG
tmpIndex = state.index
IF StrExpression&(exp$, state) = 0 THEN
' If StrExpression consumed any tokens and failed, then it's a real error
IF state.index <> tmpIndex THEN EXIT FUNCTION
IF NumericExpression&(exp$, state) = 0 THEN EXIT FUNCTION
END IF
IF state.result <> "" THEN pushelement state.result, ","
IF (state.num.typ AND ISSTRING) THEN
pushelement state.result, state.num.s
ELSEIF (state.num.typ AND ISFLOAT) THEN
pushelement state.result, _TRIM$(STR$(state.num.f))
ELSEIF (state.num.typ AND ISUNSIGNED) THEN
pushelement state.result, _TRIM$(STR$(state.num.ui)) + "~&&"
ELSE
pushelement state.result, _TRIM$(STR$(state.num.i)) + "&&"
END IF
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF ele$ = "," THEN _CONTINUE
' If we parsed all the tokens in the string, state.index should be -1
'
' If there are still tokens left and it's not a comma, then something
' is broken
IF state.index = -1 THEN
CommaExpression& = -1
ELSE
state.errStr = "ERROR - Unexpected element '" + ele$ + "'"
END IF
EXIT FUNCTION
LOOP
END FUNCTION
FUNCTION StrExpression&(exp$, state AS ParserState)
IF ParseString&(exp$, state) = 0 THEN EXIT FUNCTION
DIM s AS STRING
s = state.num.s
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "+" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF ParseString&(exp$, state) = 0 THEN FixupErrorMessage state, "+": EXIT FUNCTION
s = elementStringConcat$(s, state.num.s)
ELSE
state.num.s = s
StrExpression& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION ParseString&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "ParseString"
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF elementIsString(ele$) THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
ParseNumSetS state.num, ele$
ParseString& = -1
ELSE
IF ParseNumHashLookup&(ele$, state) THEN
IF (state.num.typ AND ISSTRING) = 0 THEN state.errStr = "ERROR - Expecting a string value": EXIT FUNCTION
ele$ = getnextelement$(exp$, state.index, state.strIndex)
ParseString& = -1
EXIT FUNCTION
END IF
state.errStr = "ERROR - Unexpected element '" + ele$ + "'"
END IF
END FUNCTION
FUNCTION NumericExpression&(exp$, state AS ParserState)
NumericExpression& = LogicalImp&(exp$, state)
END FUNCTION
FUNCTION LogicalImp&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalImp"
IF LogicalEqv&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "IMP" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF LogicalEqv&(exp$, state) = 0 THEN FixupErrorMessage state, "IMP": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui IMP state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i IMP state.num.i
END IF
ELSE
state.num = num
LogicalImp& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION LogicalEqv&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalEqv"
IF LogicalXor&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "EQV" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF LogicalXor&(exp$, state) = 0 THEN FixupErrorMessage state, "EQV": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui EQV state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i EQV state.num.i
END IF
ELSE
state.num = num
LogicalEqv& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION LogicalXor&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalXor"
IF LogicalOr&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "XOR" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF LogicalOr&(exp$, state) = 0 THEN FixupErrorMessage state, "XOR": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui XOR state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i XOR state.num.i
END IF
ELSE
state.num = num
LogicalXor& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION LogicalOr&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalOr"
IF LogicalAnd&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "OR" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF LogicalAnd&(exp$, state) = 0 THEN FixupErrorMessage state, "OR": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui OR state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i OR state.num.i
END IF
ELSE
state.num = num
LogicalOr& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION LogicalAnd&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalAnd"
IF LogicalNot&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "AND" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF LogicalNot&(exp$, state) = 0 THEN FixupErrorMessage state, "AND": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui AND state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i AND state.num.i
END IF
ELSE
state.num = num
LogicalAnd& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION LogicalNot&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "LogicalNot"
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "NOT" THEN ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Relation&(exp$, state) = 0 THEN FixupErrorMessage state, "NOT": EXIT FUNCTION
IF ele$ = "NOT" THEN
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI state.num, UINTEGER64TYPE - ISPOINTER, NOT state.num.ui
ELSE
ParseNumSetI state.num, INTEGER64TYPE - ISPOINTER, NOT state.num.i
END IF
END IF
LogicalNot& = -1
END FUNCTION
FUNCTION Relation&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Relation"
IF Term&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "<>" OR ele$ = "><" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, "<>": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f <> state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui <> state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i <> state.num.i
END IF
ELSEIF ele$ = ">=" OR ele$ = "=>" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, ">=": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f >= state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui >= state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i >= state.num.i
END IF
ELSEIF ele$ = "<=" OR ele$ = "=<" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, "<=": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f <= state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui <= state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i <= state.num.i
END IF
ELSEIF ele$ = "<" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, "<": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f < state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui < state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i < state.num.i
END IF
ELSEIF ele$ = ">" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, ">": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f > state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui > state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i > state.num.i
END IF
ELSEIF ele$ = "=" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Term&(exp$, state) = 0 THEN FixupErrorMessage state, "=": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f = state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui = state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i = state.num.i
END IF
ELSE
state.num = num
Relation& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION Term&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Term"
IF ParseMod&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "+" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF ParseMod&(exp$, state) = 0 THEN FixupErrorMessage state, "+": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f + state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui + state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i + state.num.i
END IF
ELSEIF ele$ = "-" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF ParseMod&(exp$, state) = 0 THEN FixupErrorMessage state, "-": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f - state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui - state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i - state.num.i
END IF
ELSE
state.num = num
Term& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION ParseMod&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "ParseMod"
IF IntDiv&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "MOD" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF IntDiv&(exp$, state) = 0 THEN FixupErrorMessage state, "MOD": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui MOD state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i MOD state.num.i
END IF
ELSE
IF CONST_EVAL_DEBUG THEN _Echo "ParseMod done!"
state.num = num
ParseMod& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTIOn
FUNCTION IntDiv&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "IntDiv"
IF Factor&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "\" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Factor&(exp$, state) = 0 THEN FixupErrorMessage state, "\": EXIT FUNCTION
IF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui \ state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i \ state.num.i
END IF
ELSE
IF CONST_EVAL_DEBUG THEN _Echo "IntDiv done!"
state.num = num
IntDiv& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION Factor&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Factor"
IF Unary&(exp$, state) = 0 THEN EXIT FUNCTION
DIM num As ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "*" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Unary&(exp$, state) = 0 THEN FixupErrorMessage state, "*": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f * state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui * state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i * state.num.i
END IF
ELSEIF ele$ = "/" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Unary&(exp$, state) = 0 THEN FixupErrorMessage state, "/": EXIT FUNCTION
' Regular division is always done as floating-point
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f / state.num.f
ELSE
IF CONST_EVAL_DEBUG THEN _Echo "Factor done!"
state.num = num
Factor& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION Unary&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Unary"
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF ele$ = "-" THEN ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF Exponent&(exp$, state) = 0 THEN FixupErrorMessage state, "-": EXIT FUNCTION
IF ele$ = "-" THEN ParseNumSetI state.num, INTEGER64TYPE - ISPOINTER, -state.num.i
Unary& = -1
IF CONST_EVAL_DEBUG THEN _Echo "Unary done!"
END FUNCTION
FUNCTION Exponent&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Exponent"
IF Numeric&(exp$, state) = 0 THEN EXIT FUNCTION
IF CONST_EVAL_DEBUG THEN _Echo "Check exponent"
DIM num AS ParseNum
num = state.num
DO
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF CONST_EVAL_DEBUG THEN _Echo "Exponent ele! " + ele$
IF ele$ = "^" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
' Right associative - 2 ^ 3 ^ 6 is (2 ^ (3 ^ 6))
'
' Unary accounts for the special case of `2 ^ -6`, where negative
' applies before the exponent
IF Unary&(exp$, state) = 0 THEN FixupErrorMessage state, "^": EXIT FUNCTION
IF (num.typ AND ISFLOAT) OR (state.num.typ AND ISFLOAT) THEN
ParseNumSetF num, FLOATTYPE - ISPOINTER, num.f ^ state.num.f
ELSEIF (num.typ AND ISUNSIGNED) OR (state.num.typ AND ISUNSIGNED) THEN
ParseNumSetUI num, UINTEGER64TYPE - ISPOINTER, num.ui ^ state.num.ui
ELSE
ParseNumSetI num, INTEGER64TYPE - ISPOINTER, num.i ^ state.num.i
END IF
ELSEIF ele$ = "ROOT" THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
' Right associative - 2 ROOT 3 ROOT 6 is (2 ROOT (3 ROOT 6))
'
' Unary accounts for the special case of `2 ROOT -6`, where negative
' applies before the exponent
IF Unary&(exp$, state) = 0 THEN FixupErrorMessage state, "ROOT": EXIT FUNCTION
IF num.f < 0 AND state.num.f >= 1 THEN sig = -1: num.f = -num.f ELSE sig = 1
expon## = (1## / state.num.f)
IF expon## <> INT(expon##) AND state.num.f < 1 THEN sig = SGN(num.f): num.f = ABS(num.f)
ParseNumSetF num, FLOATTYPE - ISPOINTER, sig * (num.f ^ expon##)
ELSE
IF CONST_EVAL_DEBUG THEN _Echo "Exponent done!"
state.num = num
Exponent& = -1
EXIT FUNCTION
END IF
LOOP
END FUNCTION
FUNCTION Numeric&(exp$, state AS ParserState)
IF CONST_EVAL_DEBUG THEN _Echo "Numeric"
ele$ = peeknextelement$(exp$, state.index, state.strIndex)
IF CONST_EVAL_DEBUG THEN _Echo "Numeric peek ele: " + ele$
IF elementIsNumber(ele$) THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
state.num.typ = elementGetNumericValue(ele$, state.num.f, state.num.i, state.num.ui)
Numeric& = -1
ELSEIF ele$ = "_PI" OR (qb64prefix_set = 1 AND ele$ = "PI") THEN
ele$ = getnextelement$(exp$, state.index, state.strIndex)
ParseNumSetF state.num, FLOATTYPE - ISPOINTER, 3.14159265358979323846264338327950288##
Numeric& = -1
EXIT FUNCTION
ELSE
IF ParseNumHashLookup&(ele$, state) THEN
IF state.num.typ AND ISSTRING THEN state.errStr = "ERROR - String can not be in numeric operation": EXIT FUNCTION
ele$ = getnextelement$(exp$, state.index, state.strIndex)
IF CONST_EVAL_DEBUG THEN _Echo "Consumed ele: " + ele$
Numeric& = -1
EXIT FUNCTION
END IF
state.errStr = "ERROR - Unexpected element '" + ele$ + "'"
state.num.s = ele$
END IF
END FUNCTION
FUNCTION ParseNumHashLookup&(ele$, state AS ParserState)
' Attempt hash lookup of existing CONST name
' CONST can be global or belong to the current sub/function
hashfound = 0
hashname$ = ele$
unusedSymbol$ = tryRemoveSymbol$(hashname$)
IF CONST_EVAL_DEBUG THEN _Echo "hash lookup: " + hashname$
IF CONST_EVAL_DEBUG THEN _Echo "unused symbol: " + hashname$
hashchkflags = HASHFLAG_CONSTANT
hashres = HashFindRev(hashname$, hashchkflags, hashresflags, hashresref)
DO WHILE hashres
IF constsubfunc(hashresref) = subfuncn OR constsubfunc(hashresref) = 0 THEN
IF constdefined(hashresref) THEN
hashfound = 1
EXIT DO
END IF
END IF
IF hashres <> 1 THEN hashres = HashFindCont(hashresflags, hashresref) ELSE hashres = 0
LOOP
IF CONST_EVAL_DEBUG THEN _Echo "Hashfound: " + str$(hashfound)
IF hashfound THEN
IF CONST_EVAL_DEBUG THEN _Echo "is string: " + str$(consttype(hashresref) AND ISSTRING)
IF consttype(hashresref) AND ISSTRING THEN
ParseNumSetS state.num, conststring(hashresref)
ELSEIF consttype(hashresref) AND ISFLOAT THEN
ParseNumSetF state.num, consttype(hashresref), constfloat(hashresref)
ELSE
IF consttype(hashresref) AND ISUNSIGNED THEN
ParseNumSetUI state.num, consttype(hashresref), constuinteger(hashresref)
ELSE
ParseNumSetI state.num, consttype(hashresref), constinteger(hashresref)
END IF
END IF
IF CONST_EVAL_DEBUG THEN _Echo "Found! value: " + str$(state.num.f) + state.num.s
ParseNumHashLookup& = -1
EXIT FUNCTION
END IF
END FUNCTION
SUB ParseNumSetF (num AS ParseNum, t AS LONG, f AS _FLOAT)
num.s = ""
num.f = f
num.i = f
num.ui = f
num.typ = t
END SUB
SUB ParseNumSetI (num AS ParseNum, t AS LONG, i AS _INTEGER64)
num.s = ""
num.i = i
num.ui = i
num.f = i
num.typ = t
END SUB
SUB ParseNumSetUI (num AS ParseNum, t AS LONG, ui AS _UNSIGNED _INTEGER64)
num.ui = ui
num.i = ui
num.f = ui
num.typ = t
END SUB
SUB ParseNumSetS (num AS ParseNum, s AS STRING)
num.s = s
num.typ = STRINGTYPE
END SUB
SUB FixupErrorMessage (state AS ParserState, op AS STRING)
IF state.num.s = "" THEN state.errStr = "ERROR - Expected variable/value after '" + op + "'"
END SUB
SUB ParseExpression2 (exp$)
exp$ = DWD(exp$)
DIM state AS ParserState
state.index = 0
state.strIndex = 0
state.num.f = 0
state.num.i = 0
state.num.typ = 0
state.errStr = ""
state.result = ""
res& = CommaExpression&(exp$, state)
IF CONST_EVAL_DEBUG THEN _Echo "res: " + STR$(res&)
IF CONST_EVAL_DEBUG THEN _Echo "resulting string: " + state.result
IF CONST_EVAL_DEBUG THEN _Echo "resulting err: " + state.errStr
IF res& = 0 THEN
exp$ = state.errStr
ELSE
exp$ = state.result
END IF
END SUB
SUB Set_ConstFunctions
REDIM ConstFuncs(10000) AS ConstFunction
'Functions with PL 10
i = i + 1: ConstFuncs(i).nam = "_PI": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ACOS": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ASIN": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ARCSEC": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ARCCSC": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ARCCOT": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_SECH": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_CSCH": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_COTH": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "COS": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "SIN": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "TAN": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "LOG": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "EXP": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "ATN": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "SQR": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_D2R": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_D2G": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_R2D": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_R2G": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_G2D": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_G2R": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "ABS": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "SGN": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "INT": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ROUND": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_CEIL": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "FIX": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_SEC": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_CSC": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_COT": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_RGB32": ConstFuncs(i).ArgCount = -1
i = i + 1: ConstFuncs(i).nam = "_RGBA32": ConstFuncs(i).ArgCount = 4
i = i + 1: ConstFuncs(i).nam = "_RGBA": ConstFuncs(i).ArgCount = 5
i = i + 1: ConstFuncs(i).nam = "_RGB": ConstFuncs(i).ArgCount = 4
i = i + 1: ConstFuncs(i).nam = "_RED32": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_GREEN32": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_BLUE32": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_ALPHA32": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "_RED": ConstFuncs(i).ArgCount = 2
i = i + 1: ConstFuncs(i).nam = "_GREEN": ConstFuncs(i).ArgCount = 2
i = i + 1: ConstFuncs(i).nam = "_BLUE": ConstFuncs(i).ArgCount = 2
i = i + 1: ConstFuncs(i).nam = "_ALPHA": ConstFuncs(i).ArgCount = 2
i = i + 1: ConstFuncs(i).nam = "CHR$": ConstFuncs(i).ArgCount = 1
i = i + 1: ConstFuncs(i).nam = "ASC": ConstFuncs(i).ArgCount = -1
REDIM _PRESERVE ConstFuncs(i) AS ConstFunction
END SUB
' args should be an element list of the arguments separated by commas
'
' Each argument should be a single element
FUNCTION EvaluateFunction$ (p, args AS STRING)
DIM n1 AS _FLOAT, nstr AS STRING
Dim argCount As Long, args(5) As ParseNum, origArgs(5) As String
argCount = countFunctionElements(args)
IF CONST_EVAL_DEBUG THEN _Echo "argCount: " + str$(argCount)
IF ConstFuncs(p).ArgCount > 0 AND argCount <> ConstFuncs(p).ArgCount THEN
EvaluateFunction$ = "ERROR - Wrong number of arguments provided to " + ConstFuncs(p).nam + "!"
EXIT FUNCTION
END IF
FOR i = 1 to argCount
ele$ = getelement$(args, 1 + (i - 1) * 2)
origArgs(i) = ele$
IF CONST_EVAL_DEBUG THEN _Echo "arg is string: " + STR$(elementIsString(ele$)) + ", argCount: " + STR$(ConstFuncs(p).ArgCount)
IF elementIsNumber(ele$) THEN
' skip the commas
args(i).typ = elementGetNumericValue&(ele$, args(i).f, args(i).i, args(i).ui)
ELSEIF elementIsString(ele$) AND ConstFuncs(p).ArgCount < 0 THEN ' positive arg count means arguments are all numbers
args(i).typ = elementGetStringValue&(ele$, args(i).s)
ELSE
EvaluateFunction$ = "ERROR - Unexpected argument: " + ele$
EXIT FUNCTION
END IF
IF CONST_EVAL_DEBUG THEN _Echo "Argument: " + str$(args(i).f) + ", str: " + getelement$(args, 1 + (i - 1) * 2)
NEXT
' Default type, some functions return different types
typ& = FLOATTYPE - ISPOINTER
SELECT CASE ConstFuncs(p).nam 'Depending on our operator..
CASE "_PI"
'Future compatible in case something ever stores extra digits for PI
n1 = 3.14159265358979323846264338327950288## * args(1).f
CASE "_ACOS": n1 = _ACOS(args(1).f)
CASE "_ASIN": n1 = _ASIN(args(1).f)
CASE "_ARCSEC"
IF ABS(args(1).f) < 1 THEN EvaluateFunction$ = "ERROR - ABS(_ARCSEC) value < 1": EXIT FUNCTION
n1 = _ARCSEC(args(1).f)
CASE "_ARCCSC"
IF ABS(args(1).f) < 1 THEN EvaluateFunction$ = "ERROR - ABS(_ARCCSC) value < 1": EXIT FUNCTION
n1 = _ARCCSC(args(1).f)
CASE "_ARCCOT": n1 = _ARCCOT(args(1).f)
CASE "_SECH": n1 = _SECH(args(1).f)
CASE "_CSCH": n1 = _CSCH(args(1).f)
CASE "_COTH": n1 = _COTH(args(1).f)
CASE "_RGB32"
typ& = INTEGER64TYPE - ISPOINTER
SELECT CASE argCount
CASE 1
n1 = _RGB32(args(1).ui, args(1).ui, args(1).ui)
CASE 2
n1 = _RGB32(args(1).ui, args(1).ui, args(1).ui, args(2).ui)
CASE 3
n1 = _RGB32(args(1).ui, args(2).ui, args(3).ui)
CASE 4
n1 = _RGB32(args(1).ui, args(2).ui, args(3).ui, args(4).ui)
CASE ELSE
EvaluateFunction$ = "ERROR - Invalid comma count (" + args + ")"
EXIT FUNCTION
END SELECT
CASE "_RGBA32"
typ& = INTEGER64TYPE - ISPOINTER
'we have to have 3 commas; not more, not less.
n1 = _RGBA32(args(1).ui, args(2).ui, args(3).ui, args(4).ui)
CASE "_RGB"
typ& = INTEGER64TYPE - ISPOINTER
'we have to have 3 commas; not more, not less.
SELECT CASE args(4).ui
CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
CASE ELSE
EvaluateFunction$ = "ERROR - Invalid Screen Mode (" + STR$(args(4).ui) + ")": EXIT FUNCTION
END SELECT
t = _NEWIMAGE(1, 1, args(4).ui)
n1 = _RGB(args(1).ui, args(2).ui, args(3).ui, t)
_FREEIMAGE t
CASE "_RGBA"
typ& = INTEGER64TYPE - ISPOINTER
'we have to have 4 commas; not more, not less.
SELECT CASE args(5).ui
CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
CASE ELSE
EvaluateFunction$ = "ERROR - Invalid Screen Mode (" + STR$(args(5).ui) + ")": EXIT FUNCTION
END SELECT
t = _NEWIMAGE(1, 1, args(5).ui)
n1 = _RGBA(args(1).ui, args(2).ui, args(3).ui, args(4).ui, t)
_FREEIMAGE t
CASE "_RED", "_GREEN", "_BLUE", "_ALPHA"
typ& = INTEGER64TYPE - ISPOINTER
SELECT CASE args(2).i
CASE 0 TO 2, 7 TO 13, 256, 32 'these are the good screen values
CASE ELSE
EvaluateNumbers$ = "ERROR - Invalid Screen Mode (" + STR$(args(2).i) + ")": EXIT FUNCTION
END SELECT
t = _NEWIMAGE(1, 1, args(2).i)
SELECT CASE ConstFuncs(p).nam
CASE "_RED": n1 = _RED(args(1).i, t)
CASE "_BLUE": n1 = _BLUE(args(1).i, t)
CASE "_GREEN": n1 = _GREEN(args(1).i, t)
CASE "_ALPHA": n1 = _ALPHA(args(1).i, t)
END SELECT
_FREEIMAGE t
CASE "_RED32", "_GREEN32", "_BLUE32", "_ALPHA32"
typ& = INTEGER64TYPE - ISPOINTER
SELECT CASE ConstFuncs(p).nam
CASE "_RED32": n1 = _RED32(args(1).i)
CASE "_BLUE32": n1 = _BLUE32(args(1).i)
CASE "_GREEN32": n1 = _GREEN32(args(1).i)
CASE "_ALPHA32": n1 = _ALPHA32(args(1).i)
END SELECT
CASE "COS": n1 = COS(args(1).f)
CASE "SIN": n1 = SIN(args(1).f)
CASE "TAN": n1 = TAN(args(1).f)
CASE "LOG": n1 = LOG(args(1).f)
CASE "EXP": n1 = EXP(args(1).f)
CASE "ATN": n1 = ATN(args(1).f)
CASE "SQR": n1 = SQR(args(1).f)
CASE "_D2R": n1 = 0.0174532925 * args(1).f
CASE "_D2G": n1 = 1.1111111111 * args(1).f
CASE "_R2D": n1 = 57.2957795 * args(1).f
CASE "_R2G": n1 = 0.015707963 * args(1).f
CASE "_G2D": n1 = 0.9 * args(1).f
CASE "_G2R": n1 = 63.661977237 * args(1).f
CASE "ABS": n1 = ABS(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "SGN": n1 = SGN(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "INT": n1 = INT(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "_ROUND": n1 = _ROUND(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "_CEIL": n1 = _CEIL(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "FIX": n1 = FIX(args(1).f): typ& = INTEGER64TYPE - ISPOINTER
CASE "_SEC": n1 = _SEC(args(1).f)
CASE "_CSC": n1 = _CSC(args(1).f)
CASE "_COT": n1 = _COT(args(1).f)
CASE "CHR$":
IF args(1).ui > 255 THEN EvaluateFunction$ = "ERROR - Invalid argument to CHR$, valid range is 0-255: " + origArgs(1): EXIT FUNCTION
nstr = CHR$(args(1).ui)
typ& = STRINGTYPE
CASE "ASC":
IF argCount < 1 OR argCount > 2 THEN EvaluateNumbers$ = "ERROR - Wrong number of arguments provided to ASC$": EXIT FUNCTION
IF (args(1).typ AND ISSTRING) = 0 THEN EvaluateFunction$ = "ERROR - Unexpected argument: '" + origArgs(1) + "'": EXIT FUNCTION
IF argCount = 1 THEN
n1 = ASC(args(1).s)
ELSE
IF args(2).typ AND ISSTRING THEN EvaluateFunction$ = "ERROR - Expected integer argument: '" + origArgs(2) + "'": EXIT FUNCTION
n1 = ASC(args(1).s, args(2).i)
END IF
typ& = INTEGER64TYPE - ISPOINTER
END SELECT
IF typ& AND ISSTRING THEN
EvaluateFunction$ = createElementString$(nstr)
ELSEIF typ& AND ISFLOAT THEN
EvaluateFunction$ = _TRIM$(STR$(n1))
ELSE
n&& = n1
EvaluateFunction$ = _TRIM$(STR$(n&&)) + "&&"
END IF
END FUNCTION
FUNCTION DWD$ (exp$) 'Deal With Duplicates
'To deal with duplicate operators in our code.
'Such as -- becomes a +
'++ becomes a +
'+- becomes a -
'-+ becomes a -
t$ = exp$
FOR l = 1 TO numelements(t$) - 1
ele$ = getelement$(t$, l)
nextele$ = getelement$(t$, l + 1)
IF ele$ = "+" AND nextele$ = "+" THEN
removeelement t$, l
l = l - 1
ELSEIF ele$ = "-" AND nextele$ = "-" THEN
removeelements t$, l, l + 1, 0
insertelements t$, l - 1, "+"
l = l - 1
ELSEIF ele$ = "-" AND nextele$ = "+" THEN
removeelement t$, l + 1
l = l - 1
ELSEIF ele$ = "+" AND nextele$ = "-" THEN
removeelement t$, l
l = l - 1
END IF
NEXT
DWD$ = t$
END FUNCTION
SUB PreParse (e$)
t$ = e$ 'preserve the original string
t$ = eleucase$(t$)
IF t$ = "" THEN e$ = "ERROR - NULL string; nothing to evaluate": EXIT SUB
'ERROR CHECK by counting our brackets
count = numelements(t$)
FOR l = 1 to count
ele$ = getelement$(t$, l)
IF ele$ = "(" THEN c = c + 1
IF ele$ = ")" THEN c = c - 1
IF c < 0 THEN e$ = "ERROR - Bad Parenthesis, too many )": EXIT SUB
NEXT
IF c <> 0 THEN e$ = "ERROR - Bad Parenthesis": EXIT SUB
'Modify so that NOT will process properly
FOR l = 1 to numelements(t$)
'FIXME: This doesn't account for `x ^ NOT y + 2`, where it evaluates as `x ^ (NOT y) + 2`
IF getelement$(t$, l) = "NOT" THEN
FOR l2 = l to numelements(t$)
ele$ = getelement$(t$, l2)
IF ele$ = "AND" OR ele$ = "OR" OR ele$ = "XOR" OR ele$ = "EQV" OR ele$ = "IMP" OR ele$ = ")" THEN
EXIT FOR
END IF
NEXT
insertelements t$, l2 - 1, ")"
insertelements t$, l - 1, "("
l = l + 1
END IF
NEXT
e$ = t$
END SUB
' Returns 0 if given element is not the name of a function
' If it is a function, the ConstFuncs() array index of the function is returned
FUNCTION IsFunctionIdentifier&(ele$)
FOR i = 1 TO UBOUND(ConstFuncs)
IF ele$ = ConstFuncs(i).nam THEN
IsFunctionIdentifier& = i
EXIT FUNCTION
ELSE
IF LEFT$(ConstFuncs(i).nam, 1) = "_" AND qb64prefix_set = 1 THEN
'try without prefix
IF ele$ = MID$(ConstFuncs(i).nam, 2) THEN
IsFunctionIdentifier& = i
EXIT FUNCTION
END IF
END IF
END IF
NEXT
END FUNCTION