DracScript 1.06 Released! LGPL Scripting Language
Nifty.
I'm unsure of what other areas need enhancements so if there's a procedure you'd like me to look at, just let me know. I enjoy a good challenge at trying to optimize things ^_^
Like this small procedure update...
Althought I don't think you're using the procedure yet. If you do, it should be roughly twice as fast as your existing procedure. It has the added bonus of performing an inplace trim as well.
I'm unsure of what other areas need enhancements so if there's a procedure you'd like me to look at, just let me know. I enjoy a good challenge at trying to optimize things ^_^
Like this small procedure update...
Code: Select all
Procedure.s TrimFloat(value.s)
;
HoldFloat.f
;
HoldString.s
;
*HoldChar.BYTE
;
HasDecimal.l
;
LastNumeric.l = 1
;
HoldBegin.l
;
If IsNumber(value, 46, 0, 0)
;
*HoldChar = @value
;
Repeat
;
If *HoldChar\b = 46
;
HasDecimal = LastNumeric
;
ElseIf *HoldChar\b = 0
;
If HasDecimal
;
ProcedureReturn PeekS(@value + HoldBegin - 1, LastNumeric)
;
Else
;
ProcedureReturn value
;
EndIf
;
Else
;
If *HoldChar\b <> 32
;
If HoldBegin = 0 : HoldBegin = *HoldChar - @value + 1 : EndIf
;
If *HoldChar\b <> 48 : LastNumeric + 1 : EndIf
;
EndIf
;
EndIf
;
*HoldChar + 1
;
ForEver
;
EndIf
;
ProcedureReturn value
;
EndProcedure
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
-
- Enthusiast
- Posts: 731
- Joined: Wed Apr 21, 2004 7:12 pm
Well not nessicarly - you could just (internally) convert a for/next loop to a While loop (just like you showed) and that way you'll save end users some time while keeping the interpreter as fast as usual.
Of course, you'd need to convert the line just the once - otherwise you will get some slow down
.
Of course, you'd need to convert the line just the once - otherwise you will get some slow down

~I see one problem with your reasoning: the fact is thats not a chicken~
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
-
- Enthusiast
- Posts: 731
- Joined: Wed Apr 21, 2004 7:12 pm
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
-
- Enthusiast
- Posts: 731
- Joined: Wed Apr 21, 2004 7:12 pm
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
Just a quick note:
For those unfamiliar witht he LGPL. If you use this code in any way you must release the modifications to the code and within your programs documentation or credits must make available a way for the source code to be obtained at no cost. Thats it in a nutshell but the full text can be found on www.gnu.org
Enjoy!
For those unfamiliar witht he LGPL. If you use this code in any way you must release the modifications to the code and within your programs documentation or credits must make available a way for the source code to be obtained at no cost. Thats it in a nutshell but the full text can be found on www.gnu.org
Enjoy!
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
-
- Addict
- Posts: 1648
- Joined: Mon Sep 20, 2004 3:52 pm
- Contact:
Hmm, the newest download have a bug, it's trying to reference a commented out array ... So I uncommented it while I am converting over to 4.0. I think it's around line 130.
Code: Select all
Dim DS_ScriptArray.DS_LINE(i,j)
4.0 code ... only a few changes 
DracScript.pbi
Now, in order to add script defined constants, instead of program defined constants, would it be possible to ...
When going line by line in the script, we look for a Left(Text, 1) = "#", then we do a split on = and left side is script name, while right side is value. Thus basically a global non-modifiable variable?
But would need to be script specific, or we could have a global constant ... hmm, possiblities, oh the fun!

DracScript.pbi
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.01 Beta"
#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
; Shannara: Fixed below, it was commented out, but is required in code.
Global Dim 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=""
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)," ")
ForEach DS_ConstantList()
DS_ScriptArray(index,i-1)\line=ReplaceString(DS_ScriptArray(index,i-1)\line,#DS_CONSTANT_CHAR+DS_ConstantList()\name,DS_ConstantList()\Value)
Next
Next
;preprocess labels and goto's, constants
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
;constants
; ForEach DS_ConstantList()
; DS_ScriptArray(index,i)\line=ReplaceString(DS_ScriptArray(index,i)\line,#DS_CONSTANT_CHAR+DS_ConstantList()\name,DS_ConstantList()\value)
; Next
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
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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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_ProcessConstants(DS_ProcessSymbols(DS_ScriptArray(script,line)\lhs))
rhs.s=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
;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
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)
If file<>""
DS_Initialize(3,100)
DS_Reset(0,1,1,0,1)
scriptnum=DS_LoadScriptFile(file,0,-1)
startms=ElapsedMilliseconds()
r=DS_Execute(scriptnum)
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
End
CompilerEndIf
When going line by line in the script, we look for a Left(Text, 1) = "#", then we do a split on = and left side is script name, while right side is value. Thus basically a global non-modifiable variable?
But would need to be script specific, or we could have a global constant ... hmm, possiblities, oh the fun!