Updated 1.04 to PB4/Windows. Besides changing linklists to global, and reinstating the array on line 130 (needed unfortunately for pb4), and the read file with new syntax ... that's is all ...
Code: Select all
;DracScript Include
;Copyright 2006 DracSoft
;If you use this file in a project it would be appreciated if you
;send me a friendly e-mail or perhaps paypal donate to: cyberneticwraith@phreaker.net
;Do not change this header!
;
; Licensed under the LGPL (www.gnu.org) with an exception: May be statically linked and
; included into a program, code must maintain the above header. Be sure to release any
; optimizations to the language ASAP!
;
; Only this file or files it includes must have code released under LGPL,
; so this CAN be used in commercial projects!
;
; Some of the math evaluation algo's are modified by DracSoft! Thanks to all!
;TODO: Make DS_RegisterDLL() procedure that calls a DS_Plugin_RegisterAll() function within the dll to load new functions
;TODO: Optimize (of course, this ToDo will be permanent =P).
;TODO: Make it so strings with the ' character in them can be used
;TODO: Add ability to declare and call procedures made in script (For now IncludeFile can achieve a similar effect)
;COMPILER CONSTANTS
#DS_TEST_MATHSPEED=0 ;set to zero to skip the eval algorithm speed test
#DS_TEST_BUILD=0 ;set to zero for use in normal projects.
#DS_INCLUDE_DEFAULT=1 ;set to zero to strip default DracScript functions from your project
Enumeration 1 ; EVAL_ALGO
#DS_EVAL_ALGO_XOMBIE
#DS_EVAL_ALGO_XOMBIENEW
#DS_EVAL_ALGO_SROD
#DS_EVAL_ALGO_TROND
EndEnumeration
#DS_EVAL_ALGO=#DS_EVAL_ALGO_XOMBIENEW
;CONSTANTS
#DS_VERSION="1.04"
#DS_RETURN_SYMBOL="RET"
#DS_CONSTANT_CHAR="#"
#DS_ERROR_STRING="#"
Enumeration ; TYPE
#DS_TYPE_NULL
#DS_TYPE_VALUE
#DS_TYPE_STRING
EndEnumeration
Enumeration 1 ; RESULT
#DS_RESULT_SUCCESS
#DS_RESULT_SYMBOL
#DS_RESULT_COMMANDERROR
#DS_RESULT_PCOUNT
#DS_RESULT_NOTEXIST
#DS_RESULT_CONDITIONAL
#DS_RESULT_NOENDIF
#DS_RESULT_NOWEND
#DS_RESULT_NOWHILE
#DS_RESULT_MATH
#DS_RESULT_NOSCRIPT
EndEnumeration
Enumeration 1 ; KEYWORD
#DS_KEYWORD_IF
#DS_KEYWORD_EXIT
#DS_KEYWORD_GOTO
#DS_KEYWORD_LABEL
#DS_KEYWORD_ELSEIF
#DS_KEYWORD_ELSE
#DS_KEYWORD_ENDIF
#DS_KEYWORD_WHILE
#DS_KEYWORD_WEND
#DS_KEYWORD_FREE
EndEnumeration
;STRUCTURES
Structure DS_COMMAND
Address.l
params.l
name.s
EndStructure
Structure DS_SYMBOL
Value.s
name.s
type.l ;DS_TYPE_<etc>
EndStructure
Structure DS_ENVIRONMENT
scriptname.s
line.l
;linecount.l
lastError.s
maxScripts.l
maxLines.l
EndStructure
Structure DS_CONSTANT
name.s
Value.s
EndStructure
Structure DS_JUMPENTRY
src.l
dest.l
EndStructure
Structure DS_LINE
IsUsed.b ;this stores if this script index has been loaded into yet
IsLast.b ;stores if this is the last line of the script
line.s
command.s
word.s
wordval.l
exp.s
params.s
lhs.s
rhs.s
operator.s
sym.s
symexp.s
EndStructure
;GLOBALS
Global NewList DS_CommandList.DS_COMMAND()
Global NewList DS_SymbolList.DS_SYMBOL()
Global NewList DS_StackList.DS_SYMBOL()
Global NewList DS_ConstantList.DS_CONSTANT()
Global NewList DS_JumpList.DS_JUMPENTRY()
Global DS_Env.DS_ENVIRONMENT
Global Dim DS_ScriptArray.DS_LINE(0,0)
;Array DS_ScriptArray.DS_LINE(i,j) is declared in DS_Initialize()
;EXPRESSION EVAL PROCEDURES
Procedure.s DS_XS_Concatenate(exp.s)
s.s=""
While 1
p=FindString(exp,"+",1)
If p>0 And CountString(PeekS(@exp,p),"'") % 2 = 0
s=s+RemoveString( Trim(PeekS(@exp,p-1)) ,"'")
exp=Right(exp,Len(exp)-p)
ElseIf p=0
s=s+RemoveString(exp,"'")
Break
EndIf
Wend
ProcedureReturn s
EndProcedure
CompilerIf #DS_EVAL_ALGO=#DS_EVAL_ALGO_XOMBIE
IncludeFile "./Xombie.pb"
CompilerEndIf
CompilerIf #DS_EVAL_ALGO=#DS_EVAL_ALGO_SROD
IncludeFile "./Srod.pb"
CompilerEndIf
CompilerIf #DS_EVAL_ALGO=#DS_EVAL_ALGO_TROND
IncludeFile "./Trond.pb"
CompilerEndIf
CompilerIf #DS_EVAL_ALGO=#DS_EVAL_ALGO_XOMBIENEW
IncludeFile "./XombieNew.pb"
CompilerEndIf
Declare.b DS_CallCommand(name.s,params.s)
;DRACSCRIPT PROCEDURES
Procedure DS_SetLastError(err.s)
DS_Env\lastError=err
EndProcedure
Procedure.s DS_GetLastError()
ProcedureReturn DS_Env\lastError
EndProcedure
Procedure.b DS_IsNumeric(str.s)
str=Trim(str)
If Mid(str,1,1)="-"
a=2
Else
a=1
EndIf
For i = a To Len(str)
c.s=Mid(str,i,1)
If (c<"0" Or c>"9") And c<>"."
ProcedureReturn 0
EndIf
Next
ProcedureReturn 1
EndProcedure
Procedure.s DS_TrimFloat(str.s)
If DS_IsNumeric(str)
If ValF(str)=0.0
ProcedureReturn "0"
EndIf
p=Len(str)-1
While PeekB(@str+p)=48 ;checks for 0, if it is, then it continues to decrement the truncate counter
p - 1
Wend
If PeekS(@str+p+1,1)="." ; make sure it was a float and not a whole number that ends in 0 like 100
ProcedureReturn PeekS(@str,p+1)
EndIf
EndIf
ProcedureReturn str
EndProcedure
Procedure.b DS_IsCommand(cmd.s)
cmd=UCase(cmd)
ForEach DS_CommandList()
If DS_CommandList()\name = cmd
ProcedureReturn 1
EndIf
Next
ProcedureReturn 0
EndProcedure
Procedure.b DS_Initialize(maxScripts.l,maxLinesPerScript.l)
If maxScripts >= 0 And maxLinesPerScript >= 0
Dim DS_ScriptArray.DS_LINE(maxScripts-1,maxLinesPerScript-1)
DS_Env\maxScripts=maxScripts
DS_Env\maxLines=maxLinesPerScript
DS_Env\scriptname=""
DS_Env\line=0
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure.s DS_ProcessConstants(s.s)
;constants
;MessageRequester(s,"bef")
ForEach DS_ConstantList()
s=ReplaceString(s,#DS_CONSTANT_CHAR+DS_ConstantList()\name,DS_ConstantList()\Value)
Next
;MessageRequester(s,"aft")
ProcedureReturn s
EndProcedure
Procedure.b DS_Reset(commands.b,stack.b,symbols.b,const.b,env.b)
If stack
ClearList(DS_StackList())
EndIf
If commands
ClearList(DS_CommandList())
EndIf
If symbols
ClearList(DS_SymbolList())
EndIf
If const
ClearList(DS_ConstantList())
EndIf
If env
DS_Env\scriptname=""
DS_Env\line=0
EndIf
ClearList(DS_JumpList())
EndProcedure
Procedure.b DS_StackPush(sym.s, val.s, type.l)
If type>-1
AddElement(DS_StackList())
DS_StackList()\name=sym
DS_StackList()\type=type
DS_StackList()\Value=val
ProcedureReturn 1
EndIf
DS_SetLastError("Stack push failed. Invalid symbol type.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_SetSymbol(sym.s, val.s, type.l)
sym=Trim(sym)
;if type is -1 dont change the type just set the value
ForEach DS_SymbolList()
If DS_SymbolList()\name=UCase(sym)
DS_SymbolList()\value=val
If type <> -1
DS_SymbolList()\type=type
EndIf
ProcedureReturn 1
EndIf
Next
;not found symbol and if valid symbol, create it
If type<>-1 And Trim(sym)<>"" And DS_IsNumeric(sym)=0
AddElement(DS_SymbolList())
DS_SymbolList()\name=UCase(sym)
DS_SymbolList()\type=type
DS_SymbolList()\value=val
ProcedureReturn 1
EndIf
DS_SetLastError("Set symbol failed. Invalid symbol name or symbol type.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_FreeSymbol(sym.s)
ForEach DS_SymbolList()
If DS_SymbolList()\name = sym
DeleteElement(DS_SymbolList())
ProcedureReturn 1
EndIf
Next
DS_SetLastError("Free symbol failed. Symbol "+sym+" does not exist.")
ProcedureReturn 0
EndProcedure
Procedure.l DS_GetSymbol(sym.s)
ForEach DS_SymbolList()
If DS_SymbolList()\name=UCase(sym)
ProcedureReturn @DS_SymbolList()
EndIf
Next
DS_SetLastError("Get symbol failed. Symbol "+sym+" does not exist.")
ProcedureReturn 0
EndProcedure
Procedure DS_Return(val.s, type.l)
DS_SetSymbol(#DS_RETURN_SYMBOL,val,type)
EndProcedure
Procedure.b DS_RegisterCommand(procAddr.l, name.s, numParams.l)
If procAddr<>0 And Trim(name)<>"" And Val(name)=0
AddElement(DS_CommandList())
DS_CommandList()\address=procaddr
DS_CommandList()\params=numParams
DS_CommandList()\name=UCase(name)
SortStructuredList(DS_CommandList(),#PB_Sort_String,OffsetOf(DS_COMMAND\name),0)
ProcedureReturn 1
EndIf
DS_SetLastError("Register command failed. Null procedure address or invalid command name.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_RegisterConstant(name.s,val.s)
If Trim(name)<>"" And Val(name)=0
AddElement(DS_ConstantList())
DS_ConstantList()\name=UCase(name)
DS_ConstantList()\value=val
ProcedureReturn 1
EndIf
DS_SetLastError("Register constant failed. Invalid constant name.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_FreeCommand(procAddr)
ForEach DS_CommandList()
If DS_CommandList()\address=procAddr
DeleteElement(DS_CommandList())
ProcedureReturn 1
EndIf
Next
DS_SetLastError("Free command failed. Command does not exist.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_FreeCommandByName(name.s)
ForEach DS_CommandList()
If DS_CommandList()\name=UCase(name)
DeleteElement(DS_CommandList())
ProcedureReturn 1
EndIf
Next
DS_SetLastError("Free command failed. Command does not exist.")
ProcedureReturn 0
EndProcedure
Procedure.b DS_FreeConstant(name.s)
ForEach DS_ConstantList()
If UCase(DS_ConstantList()\name)=UCase(name)
DeleteElement(DS_ConstantList())
ProcedureReturn 1
EndIf
Next
DS_SetLastError("Free constant failed. Constant does not exist.")
ProcedureReturn 0
EndProcedure
Procedure.s DS_GetParameter(params.s,index)
For i = 1 To Len(params)
Char.s=Mid(params,i,1)
If Char="," And qcount % 2 = 0
ccount + 1
lastcomma=currentcomma
currentcomma=i
If ccount=index
ProcedureReturn Trim(PeekS(@params+lastcomma,currentcomma-lastcomma-1))
EndIf
EndIf
If Char="'"
qcount + 1
EndIf
Next
If i-1=Len(params) And index=ccount+1
ProcedureReturn Trim(PeekS(@params+currentcomma,Len(params)-currentcomma))
EndIf
ProcedureReturn ""
EndProcedure
Procedure.l DS_CountParameters(params.s)
For i = 1 To Len(params)
char.s=Mid(params,i,1)
If char="," And qcount % 2 = 0
ccount + 1
ElseIf char="'"
qcount + 1
EndIf
Next
If ccount=0 And Trim(params)=""
ProcedureReturn 0
EndIf
ProcedureReturn ccount+1
EndProcedure
Procedure.l DS_FindStringReverse(s.s,f.s,p.l)
p=p-Len(f)+1
While p>=0
If PeekS(@s+p,Len(f))=f
ProcedureReturn p+1
EndIf
p-1
Wend
ProcedureReturn 0
EndProcedure
Procedure.s DS_ProcessCommands(exp.s)
spos=1
parcount=0
While spos>0
spos=0
ForEach DS_CommandList()
temp=DS_FindStringReverse(UCase(exp),DS_CommandList()\name,Len(exp))
rchar.s=Mid(exp,temp+Len(DS_CommandList()\name),1)
lchar.s=Mid(exp,temp-1,1)
If rchar="(" And Mid(exp,temp+Len(DS_CommandList()\name)+1,1)=")"
parcount=DS_CommandList()\params
spos=temp
command.s=DS_CommandList()\name
ElseIf rchar=" " Or rchar="("
If lchar="=" Or lchar=">" Or lchar="<" Or lchar="+" Or lchar="-" Or lchar="/" Or lchar="*" Or lchar="^" Or lchar=" " Or lchar="," Or lchar="(" Or temp=1
qcl=CountString(Left(exp,spos-1),"'")
If qcl % 2 = 0 ;so not in the midle of a string
If temp>spos
parcount=DS_CommandList()\params
spos=temp
command.s=DS_CommandList()\name
EndIf
EndIf
EndIf
EndIf
Next
If spos>0
;get params
If parcount>0
Startpos=FindString(exp,"(",spos)
pcount=0
tmp=startpos+1
While 1
If tmp<Len(exp)
cur.s=PeekS(@exp+tmp,1)
If cur="("
pcount+1
EndIf
If cur=")" And pcount=0
Endpos=tmp
Break
ElseIf cur=")"
pcount-1
EndIf
tmp + 1
Else
DS_SetLastError("Syntax error: '(' expected after '"+command+"'.")
ProcedureReturn #DS_ERROR_STRING+"ERROR"
EndIf
Wend
Else
Startpos=FindString(exp,"(",spos)
Endpos=Startpos
EndIf
;call command
params.s=Trim(PeekS(@exp+Startpos,Endpos-Startpos))
res=DS_CallCommand(command,params)
If res=#DS_RESULT_SUCCESS
*symbol.DS_SYMBOL=DS_GetSymbol(#DS_RETURN_SYMBOL)
Select *symbol\type
Case #DS_TYPE_STRING
exp=PeekS(@exp,spos-1)+"'"+*symbol\Value+"'"+PeekS(@exp+Endpos+1,Len(exp)-Endpos)
Case #DS_TYPE_VALUE
exp=PeekS(@exp,spos-1)+*symbol\Value+PeekS(@exp+Endpos+1,Len(exp)-Endpos)
Case #DS_TYPE_NULL
exp=PeekS(@exp,spos-1)+"0"+PeekS(@exp+Endpos+1,Len(exp)-Endpos)
EndSelect
Else
ProcedureReturn #DS_ERROR_STRING+"ERROR:"+Str(res)
EndIf
EndIf
Wend
ProcedureReturn exp
EndProcedure
Procedure.b DS_CallCommand(name.s,params.s)
;push the stack
params=DS_ProcessConstants(params)
pc = DS_CountParameters(params)
For i = 1 To pc
param.s = DS_XS_Evaluate(DS_GetParameter(params,i))
If DS_IsNumeric(param)=0
type=#DS_TYPE_STRING
Else ; its a number!
type=#DS_TYPE_VALUE
EndIf
If PeekS(@param,Len(#DS_ERROR_STRING))=#DS_ERROR_STRING
DS_SetLastError("Expression evaluation error in: "+StringField(params,i,","))
ProcedureReturn #DS_RESULT_MATH
EndIf
DS_StackPush(Str(i+CountList(DS_StackList())),param,type)
Next
;execute the command
ForEach DS_CommandList()
If DS_CommandList()\name=UCase(name)
If DS_CommandList()\params=pc
If CallFunctionFast(DS_CommandList()\Address)=0
;The error text should be set by the procedure before returning
ProcedureReturn #DS_RESULT_COMMANDERROR ;if the actual function returns an error
EndIf
found=1
Break
Else
DS_SetLastError("Command error. Incorrect number of parameters. Expected: "+Str(DS_CommandList()\params))
ProcedureReturn #DS_RESULT_PCOUNT ;if the parameter count is wrong
EndIf
EndIf
Next
If found=0
DS_SetLastError("Syntax error. Command "+UCase(name)+" does not exist.")
ProcedureReturn #DS_RESULT_NOTEXIST ;if the command doesnt exist
EndIf
;pop the stack
ClearList(DS_StackList()) ; just clear it for now
; lc=CountList(DS_StackList())
; For i=1 To pc
; SelectElement(DS_StackList(),lc-i)
; DeleteElement(DS_StackList())
; Next
ProcedureReturn #DS_RESULT_SUCCESS ;function was successful
EndProcedure
Procedure.l DS_FindLabel(script.l,label.s)
i=0
While DS_ScriptArray(script,i)\IsLast=0
If UCase(Trim(DS_ScriptArray(script,i)\line))="LABEL "+UCase(Trim(label))
ProcedureReturn i
EndIf
i + 1
Wend
DS_SetLastError("Syntax error: Label '"+label+"' not found.")
ProcedureReturn -1
EndProcedure
Procedure.b DS_FreeScript(index.l)
If index>=0 And index<DS_Env\maxScripts
For i=0 To DS_Env\maxLines-1
DS_ScriptArray(index,i)\IsUsed=0 ;this stores if this script index has been loaded into yet
DS_ScriptArray(index,i)\IsLast=0 ;stores if this is the last line of the script
DS_ScriptArray(index,i)\line=""
DS_ScriptArray(index,i)\command=""
DS_ScriptArray(index,i)\word=""
DS_ScriptArray(index,i)\wordval=0
DS_ScriptArray(index,i)\exp=""
DS_ScriptArray(index,i)\params=""
DS_ScriptArray(index,i)\lhs=""
DS_ScriptArray(index,i)\rhs=""
DS_ScriptArray(index,i)\operator=""
DS_ScriptArray(index,i)\sym=""
DS_ScriptArray(index,i)\symexp=""
Next
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure.l DS_LoadScriptString(str.s,index.l)
If index=-1
;find next free script
For i=0 To DS_Env\maxScripts-1
If DS_ScriptArray(i,0)\IsUsed=0
index=i
Break
EndIf
Next
If index=-1
ProcedureReturn -1
EndIf
ElseIf index > DS_Env\maxScripts-1 Or index < -1
ProcedureReturn -1
EndIf
;count lines, check if its above the max
str=RemoveString(str,Chr(13))
linecount=CountString(str,Chr(10))+1
If linecount > DS_Env\maxLines
ProcedureReturn -1
EndIf
;clear the script we're loading over
If DS_FreeScript(index)=0
ProcedureReturn -1
EndIf
;process script into the array
For i=1 To linecount
DS_ScriptArray(index,i-1)\line=ReplaceString(Trim(StringField(str,i,Chr(10))),Chr(9)," ")
Next
;preprocess labels and goto's
For i=0 To linecount-1
If UCase(Trim(StringField(Trim(DS_ScriptArray(index,i)\line),1," ")))="GOTO"
pos=DS_FindLabel(index,Trim(StringField(Trim(DS_ScriptArray(index,i)\line),2," ")))
If pos > -1
DS_ScriptArray(index,i)\line="GOTO "+Str(pos)
Else
ProcedureReturn -1
EndIf
EndIf
Next
;preprocess other parts of the data
For i=0 To linecount-1
DS_ScriptArray(index,i)\line=Trim(DS_ScriptArray(index,i)\line)
DS_ScriptArray(index,i)\word=Trim(UCase(StringField(DS_ScriptArray(index,i)\line,1," ")))
DS_ScriptArray(index,i)\exp=Trim(Right(DS_ScriptArray(index,i)\line,Len(DS_ScriptArray(index,i)\line)-Len(DS_ScriptArray(index,i)\word)))
DS_ScriptArray(index,i)\sym=StringField(DS_ScriptArray(index,i)\line,1,"=")
DS_ScriptArray(index,i)\symexp=Trim(Right(DS_ScriptArray(index,i)\line,Len(DS_ScriptArray(index,i)\line)-Len(DS_ScriptArray(index,i)\sym)))
If Left(DS_ScriptArray(index,i)\symexp,1)="="
DS_ScriptArray(index,i)\symexp=Right(DS_ScriptArray(index,i)\symexp,Len(DS_ScriptArray(index,i)\symexp)-1)
EndIf
DS_ScriptArray(index,i)\sym=RemoveString(UCase(DS_ScriptArray(index,i)\sym), " ")
DS_ScriptArray(index,i)\command=Trim(UCase(StringField(DS_ScriptArray(index,i)\line,1,"(")))
DS_ScriptArray(index,i)\params=Trim(Right(DS_ScriptArray(index,i)\line,Len(DS_ScriptArray(index,i)\line)-Len(DS_ScriptArray(index,i)\command)))
If Mid(DS_ScriptArray(index,i)\params,1,1)="("
DS_ScriptArray(index,i)\params=PeekS(@DS_ScriptArray(index,i)\params+1)
EndIf
If Mid(DS_ScriptArray(index,i)\params,Len(DS_ScriptArray(index,i)\params),1)=")"
DS_ScriptArray(index,i)\params=PeekS(@DS_ScriptArray(index,i)\params,Len(DS_ScriptArray(index,i)\params)-1)
EndIf
;get wordval
Select DS_ScriptArray(index,i)\word
Case "IF"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_IF
Case "ELSEIF"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_ELSEIF
Case "ELSE"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_ELSE
Case "ENDIF"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_ENDIF
Case "EXIT"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_EXIT
Case "WHILE"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_WHILE
Case "WEND"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_WEND
Case "FREE"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_FREE
Case "GOTO"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_GOTO
Case "LABEL"
DS_ScriptArray(index,i)\wordval=#DS_KEYWORD_LABEL
Default
DS_ScriptArray(index,i)\wordval=0
EndSelect
;get operator and lhs,rhs
If FindString(DS_ScriptArray(index,i)\exp,"<>",1) <> 0
DS_ScriptArray(index,i)\lhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,1,"<>"),">")
DS_ScriptArray(index,i)\rhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,2,"<>"),">")
DS_ScriptArray(index,i)\operator="<>"
ElseIf FindString(DS_ScriptArray(index,i)\exp,">=",1) <> 0
DS_ScriptArray(index,i)\lhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,1,">="),"=")
DS_ScriptArray(index,i)\rhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,2,">="),"=")
DS_ScriptArray(index,i)\operator=">="
ElseIf FindString(DS_ScriptArray(index,i)\exp,"<=",1) <> 0
DS_ScriptArray(index,i)\lhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,1,"<="),"=")
DS_ScriptArray(index,i)\rhs=RemoveString(StringField(DS_ScriptArray(index,i)\exp,2,"<="),"=")
DS_ScriptArray(index,i)\operator="<="
ElseIf FindString(DS_ScriptArray(index,i)\exp,"<",1) <> 0
DS_ScriptArray(index,i)\lhs=StringField(DS_ScriptArray(index,i)\exp,1,"<")
DS_ScriptArray(index,i)\rhs=StringField(DS_ScriptArray(index,i)\exp,2,"<")
DS_ScriptArray(index,i)\operator="<"
ElseIf FindString(DS_ScriptArray(index,i)\exp,">",1) <> 0
DS_ScriptArray(index,i)\lhs=StringField(DS_ScriptArray(index,i)\exp,1,">")
DS_ScriptArray(index,i)\rhs=StringField(DS_ScriptArray(index,i)\exp,2,">")
DS_ScriptArray(index,i)\operator=">"
ElseIf FindString(DS_ScriptArray(index,i)\exp,"=",1) <> 0
DS_ScriptArray(index,i)\lhs=StringField(DS_ScriptArray(index,i)\exp,1,"=")
DS_ScriptArray(index,i)\rhs=StringField(DS_ScriptArray(index,i)\exp,2,"=")
DS_ScriptArray(index,i)\operator="="
EndIf
Next
DS_ScriptArray(index,0)\IsUsed=1
DS_ScriptArray(index,linecount)\IsLast=1
ProcedureReturn index
EndProcedure
Procedure.s DS_ReadScriptFile(file.s,removeComments.b) ;this reads an entire file and also handles includes
fid=ReadFile(#PB_Any,file)
If fid<>0
;UseFile(fid)
While Eof(fid)=0
tmp.s=ReadString(fid)
If removeComments=0 Or (Left(RemoveString(Trim(tmp),Chr(9)),1)<>";" And RemoveString(Trim(tmp),Chr(9))<>"")
If UCase(StringField(tmp,1," "))="INCLUDEFILE"
Nextfile.s=RemoveString(Trim(Right(tmp,Len(tmp)-Len("INCLUDEFILE"))),"'")
tmp=DS_ReadScriptFile(Nextfile,removeComments)
;UseFile(fid)
EndIf
str.s=str.s+tmp+Chr(10)
EndIf
Wend
CloseFile(fid)
ProcedureReturn str
Else
DS_SetLastError("Could not load the specified file: "+file)
ProcedureReturn ""
EndIf
EndProcedure
Procedure.l DS_LoadScriptFile(file.s,removeComments.b,index.l)
ProcedureReturn DS_LoadScriptString(DS_ReadScriptFile(file,removeComments),index)
EndProcedure
Procedure.s DS_ProcessSymbols(exp.s)
ForEach DS_SymbolList()
spos=1
While spos>0
spos=FindString(UCase(exp),DS_SymbolList()\name,spos)
If spos>0
char.s=Mid(exp,spos+Len(DS_SymbolList()\name),1)
char2.s=Mid(exp,spos-1,1)
If char="=" Or char=">" Or char="<" Or char="+" Or char="-" Or char="/" Or char="*" Or char="^" Or char=" " Or char="," Or char=")" Or spos+Len(DS_SymbolList()\name)-1=Len(exp)
If char2="=" Or char2=">" Or char2="<" Or char2="+" Or char2="-" Or char2="/" Or char2="*" Or char2="^" Or char2=" " Or char2="," Or char2="(" Or spos=1
qcl=CountString(Left(exp,spos-1),"'")
If qcl % 2 = 0 ;so not in the midle of a string
If DS_SymbolList()\type=#DS_TYPE_STRING
exp=PeekS(@exp,spos-1)+"'"+DS_SymbolList()\value+"'"+PeekS(@exp+spos+Len(DS_SymbolList()\name)-1)
ElseIf DS_SymbolList()\type=#DS_TYPE_VALUE
exp=PeekS(@exp,spos-1)+DS_SymbolList()\value+PeekS(@exp+spos+Len(DS_SymbolList()\name)-1)
ElseIf DS_SymbolList()\type=#DS_TYPE_NULL
exp=PeekS(@exp,spos-1)+"0"+PeekS(@exp+spos+Len(DS_SymbolList()\name)-1)
EndIf
Else
spos+1
EndIf
Else
spos+1
EndIf
Else
spos+1
EndIf
EndIf
Wend
Next
ProcedureReturn exp
EndProcedure
Procedure.b DS_EvaluateConditional(script.l,line.l)
If DS_ScriptArray(script,line)\operator="<>"
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)=ValF(r)
ProcedureReturn 0
Else
ProcedureReturn 1
EndIf
Else
If l=r
ProcedureReturn 0
Else
ProcedureReturn 1
EndIf
EndIf
EndIf
If DS_ScriptArray(script,line)\operator=">="
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)>=ValF(r)
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
Else
If l>=r
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndIf
EndIf
If DS_ScriptArray(script,line)\operator="<="
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)<=ValF(r)
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
Else
If l<=r
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndIf
EndIf
If DS_ScriptArray(script,line)\operator=">"
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)>ValF(r)
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
Else
If l>r
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndIf
EndIf
If DS_ScriptArray(script,line)\operator="<"
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)<ValF(r)
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
Else
If l<r
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndIf
EndIf
If DS_ScriptArray(script,line)\operator="="
lhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs)))
rhs.s=DS_ProcessCommands(DS_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\rhs)))
r.s=DS_XS_Evaluate(rhs)
l.s=DS_XS_Evaluate(lhs)
If DS_IsNumeric(r) And DS_IsNumeric(l)
If ValF(l)=ValF(r)
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
Else
If l=r
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndIf
EndIf
DS_SetLastError("Syntax error: Invalid operator found in expression - '"+DS_ScriptArray(script,line)\line+"'")
ProcedureReturn -1
EndProcedure
Procedure.b DS_DoJumpTable()
ForEach DS_JumpList()
If DS_Env\line = DS_JumpList()\src
DS_Env\line = DS_JumpList()\dest
ProcedureReturn 1
EndIf
Next
ProcedureReturn 0
EndProcedure
Procedure.b DS_Execute(script.l)
If script < 0 Or script >= DS_Env\maxScripts Or DS_ScriptArray(script,0)\IsUsed=0
ProcedureReturn #DS_RESULT_NOSCRIPT
EndIf
;ClearList(DS_SymbolList())
ClearList(DS_JumpList())
DS_Env\line=0
While DS_ScriptArray(script,DS_Env\line)\isLast=0 ; DS_Env\line <= DS_Env\linecount
word.s=DS_ScriptArray(script,DS_Env\line)\word
command.s=DS_ScriptArray(script,DS_Env\line)\command
wordval=DS_ScriptArray(script,DS_Env\line)\wordval
;AddToLog("Word: "+word+" | Wordval: "+Str(wordval))
;check if comment ";" or blank line
If PeekS(@word,1)<>";" And DS_ScriptArray(script,DS_Env\line)\line<>""
;check logic and keywords
If wordval=#DS_KEYWORD_EXIT
ProcedureReturn #DS_RESULT_SUCCESS
ElseIf wordval=#DS_KEYWORD_GOTO Or wordval=#DS_KEYWORD_LABEL
If wordval=#DS_KEYWORD_GOTO
DS_Env\line=Val(PeekS(@DS_ScriptArray(script,DS_Env\line)\line+4))
EndIf
ElseIf wordval=#DS_KEYWORD_IF
DS_RestartIf:
cond=DS_EvaluateConditional(script,DS_Env\line)
If cond=-1
ProcedureReturn #DS_RESULT_CONDITIONAL
ElseIf cond=0
If DS_DoJumpTable()=0
srcline=DS_Env\line
Ifcount=0
DS_Env\line + 1
While 1
target.l=DS_ScriptArray(script,DS_Env\line)\wordval
If target=#DS_KEYWORD_IF
Ifcount + 1
EndIf
If target=#DS_KEYWORD_ELSEIF And Ifcount=0
Goto DS_RestartIf
ElseIf (target=#DS_KEYWORD_ELSE Or target=#DS_KEYWORD_ENDIF) And Ifcount=0 ;no need to increment since its done at end of function
AddElement(DS_JumpList())
DS_JumpList()\src=srcline
DS_JumpList()\dest=DS_Env\line
Break
ElseIf target=#DS_KEYWORD_ENDIF
Ifcount - 1
EndIf
DS_Env\line + 1
If DS_ScriptArray(script,DS_Env\line)\IsLast
DS_SetLastError("Syntax error: Expected 'ENDIF'")
ProcedureReturn #DS_RESULT_NOENDIF
EndIf
Wend
EndIf
EndIf
ElseIf wordval=#DS_KEYWORD_ELSEIF Or wordval=#DS_KEYWORD_ELSE
If DS_DoJumpTable()=0
srcline=DS_Env\line
Ifcount=0
While 1
target.l=DS_ScriptArray(script,DS_Env\line)\wordval
If target=#DS_KEYWORD_IF
Ifcount + 1
EndIf
If target=#DS_KEYWORD_ENDIF And Ifcount=0 ;no need to increment since its done at end of function
AddElement(DS_JumpList())
DS_JumpList()\src=srcline
DS_JumpList()\dest=DS_Env\line
Break
ElseIf target=#DS_KEYWORD_ENDIF
Ifcount - 1
EndIf
DS_Env\line + 1
If DS_ScriptArray(script,DS_Env\line)\IsLast
DS_SetLastError("Syntax error: Expected 'ENDIF'")
ProcedureReturn #DS_RESULT_NOENDIF
EndIf
Wend
EndIf
ElseIf wordval=#DS_KEYWORD_ENDIF
;do nothing
ElseIf wordval=#DS_KEYWORD_WHILE
exp.s=DS_ScriptArray(script,DS_Env\line)\exp
cond=DS_EvaluateConditional(script,DS_Env\line)
If cond=0
If DS_DoJumpTable()=0
srcline=DS_Env\line
Whilecount=0
DS_Env\line + 1
While 1
target.l=DS_ScriptArray(script,DS_Env\line)\wordval
If target=#DS_KEYWORD_WHILE
Whilecount + 1
EndIf
If target=#DS_KEYWORD_WEND And Whilecount=0
AddElement(DS_JumpList())
DS_JumpList()\src=srcline
DS_JumpList()\dest=DS_Env\line
Break
ElseIf target=#DS_KEYWORD_WEND
Whilecount - 1
EndIf
DS_Env\line + 1
If DS_ScriptArray(script,DS_Env\line)\IsLast
DS_SetLastError("Syntax error: Expected 'WEND'")
ProcedureReturn #DS_RESULT_NOWEND
EndIf
Wend
EndIf
EndIf
ElseIf wordval=#DS_KEYWORD_WEND
If DS_DoJumpTable()=0
srcline=DS_Env\line
Wendcount=0
DS_Env\line - 1
While 1
target.l=DS_ScriptArray(script,DS_Env\line)\wordval
If target=#DS_KEYWORD_WEND
Wendcount + 1
EndIf
If target=#DS_KEYWORD_WHILE And Wendcount=0
DS_Env\line - 1 ;force eval of the while since we increment below
AddElement(DS_JumpList())
DS_JumpList()\src=srcline
DS_JumpList()\dest=DS_Env\line
Break
ElseIf target=#DS_KEYWORD_WHILE
Wendcount - 1
EndIf
DS_Env\line - 1
If DS_Env\line < 0
DS_SetLastError("Syntax error: Expected 'WHILE'")
ProcedureReturn #DS_RESULT_NOWHILE
EndIf
Wend
EndIf
ElseIf wordval=#DS_KEYWORD_FREE
DS_FreeSymbol(DS_ScriptArray(script,DS_Env\line)\sym)
Else
If DS_IsCommand(command)=0
;preprocess symbol values into the expression
sym.s=DS_ScriptArray(script,DS_Env\line)\sym
exp.s=DS_ProcessConstants(DS_ScriptArray(script,DS_Env\line)\symexp)
If exp=""
DS_SetLastError("Syntax error: Expression expected or unknown command.")
ProcedureReturn #DS_RESULT_NOTEXIST
EndIf
exp=DS_ProcessSymbols(exp)
exp=DS_ProcessCommands(exp)
If PeekS(@exp,Len(#DS_ERROR_STRING))=#DS_ERROR_STRING
ProcedureReturn #DS_RESULT_COMMANDERROR
EndIf
;symbol assignment/operation (a=b+c, etc)
found=0
ForEach DS_SymbolList()
If DS_SymbolList()\name = sym
tmp.s=DS_XS_Evaluate(exp)
DS_SymbolList()\value=tmp
If DS_IsNumeric(tmp)=0
DS_SymbolList()\type=#DS_TYPE_STRING
Else
DS_SymbolList()\type=#DS_TYPE_VALUE
EndIf
found=1
Break
EndIf
Next
If found=0
tmp.s=DS_XS_Evaluate(exp)
If DS_IsNumeric(tmp)=0
DS_SetSymbol(sym,tmp,#DS_TYPE_STRING)
Else
DS_SetSymbol(sym,tmp,#DS_TYPE_VALUE)
EndIf
EndIf
If PeekS(@tmp,Len(#DS_ERROR_STRING))=#DS_ERROR_STRING
DS_SetLastError("Expression evaluation error in: "+DS_ScriptArray(script,DS_Env\line)\symexp)
ProcedureReturn #DS_RESULT_MATH
EndIf
Else
;try To execute as command
params.s=DS_ProcessSymbols(DS_ScriptArray(script,DS_Env\line)\params)
params.s=DS_ProcessCommands(params)
res=DS_CallCommand(command,params)
If res<>#DS_RESULT_SUCCESS
ProcedureReturn res
EndIf
EndIf
EndIf
EndIf
;increment program counter
DS_Env\line + 1
Wend
ProcedureReturn #DS_RESULT_SUCCESS
EndProcedure
CompilerIf #DS_INCLUDE_DEFAULT=1
IncludeFile "./DracScript_Default.pb"
CompilerEndIf
;----------------------------------------------------------------------------------------------------
CompilerIf #DS_TEST_MATHSPEED=1
;Test evaluation algorithm speed
ex.s="100+1"
startms=ElapsedMilliseconds()
For i=1 To 100000
DS_XS_Evaluate(ex)
Next
MessageRequester("100,000 Additions: Total (ms)",Str(ElapsedMilliseconds()-startms))
CompilerEndIf
CompilerIf #DS_TEST_BUILD=1
;PROCEDURES FOR TESTING
Procedure.b DS_DEFAULT_MessageBox() ; 2 params
SelectElement(DS_StackList(),0)
*param1.DS_SYMBOL=@DS_StackList()
SelectElement(DS_StackList(),1)
*param2.DS_SYMBOL=@DS_StackList()
;test the param type (not required for this function)
;If *param1\type<>#DS_TYPE_VALUE Or *param2\type<>#DS_TYPE_STRING
; ProcedureReturn 0
;EndIf
;show the output window and wait for continue
If IsWindow(999)=0
OpenWindow(999,0,0,300,200,#PB_Window_TitleBar|#PB_Window_ScreenCentered,"DracScript Tester")
CreateGadgetList(WindowID(999))
ListViewGadget(991,0,0,300,180)
ButtonGadget(990,0,181,149,19,"Continue")
ButtonGadget(992,151,181,149,19,"Quit")
EndIf
AddGadgetItem(991,-1,"Param1: "+*param1\value)
AddGadgetItem(991,-1,"Param2: "+*param2\value)
AddGadgetItem(991,-1,"Current line: "+Str(DS_Env\line+1))
AddGadgetItem(991,-1,"-------------------")
Repeat
event=WaitWindowEvent()
If event=#PB_Event_Gadget And EventGadgetID()=992
End
EndIf
Until event=#PB_Event_Gadget And EventGadgetID()=990
;return now
DS_Return("",#DS_TYPE_NULL)
ProcedureReturn 1
EndProcedure
;MAIN FUNC
If DS_RegisterCommand(@DS_DEFAULT_MessageBox(),"MessageBox",2)=0
MessageRequester("","Failed to register")
EndIf
DS_RegisterDefault()
If DS_RegisterDefault()=0
MessageRequester("","Failed to register default command set")
EndIf
;Test individual commands
If DS_SetSymbol("Yo","WEeeeoooo",#DS_TYPE_STRING)=0
MessageRequester("","Failed to set symbol")
EndIf
If DS_RegisterConstant("TEST_MESSAGE","'Yo bro!'")=0
MessageRequester("","Failed to register constant")
EndIf
If DS_RegisterConstant("TEST_VALUE","2")=0
MessageRequester("","Failed to register constant")
EndIf
;Test script execution from file
;file.s=OpenFileRequester("Open a script file to test...","","DS Files|*.ds",0)
file.s="../../Dracscript/scripts/test.ds"
DS_Initialize(3,100)
For i=0 To 50
If file<>""
DS_LoadScriptFile(file,0,2)
DS_Reset(0,0,1,1,0)
DS_RegisterConstant("TEST_MESSAGE","'Yo bro!'")
startms=ElapsedMilliseconds()
r=DS_Execute(2)
If r<>#DS_RESULT_SUCCESS
MessageRequester("Error type: "+Str(r),"Error in script. Line: "+Str(DS_Env\line+1)+Chr(13)+Chr(10)+DS_GetLastError())
If IsWindow(999)
Repeat
event=WaitWindowEvent()
If event=#PB_Event_Gadget And EventGadgetID()=992
End
EndIf
ForEver
EndIf
Else
;MessageRequester("Total run time:",Str(ElapsedMilliseconds()-startms)+" ms.")
EndIf
EndIf
Next
End
CompilerEndIf