This is a veeeeeeeeeeeery simple scripting engine. The only real problem so far is the lack of functions to parse scripts and send bytecodes to the call stack for processing. Most of what I want to fix by now can be resolved through somewhat clever coding. Though I guess it's hard to be clever when you're bored out of your bloody mind.
Anyhow, the code:
No media required.
If anyone would like to try to help me to improve upon this, I'd be happy. Of course, this is another open source project of mine.
Anyhow, the code:
;; Include "VirtualFileStream.bb" ;; Virtual file stream. To be used in preprocessing newly loaded scripts. ;#Region PUSH/POP DATA Global PoppedClass% = CreateBank(0) Function PushObject(Bank,Value$,Class%=V_INT) Select Class Case V_INT,V_INTCONST,V_FLOAT,V_STRING PushInt(Bank,Int(Value)) Case V_FLOATCONST PushFloat(Bank,Float(Value)) Case V_STRINGCONST PushString(Bank,Value) Default PushInt(Bank,Int(Value)) End Select PushByte(Bank,Class) End Function Function PopObject$(Bank) Class = PopByte(Bank) PushByte(PoppedClass,Class) Select Class Case V_INT,V_INTCONST,V_FLOAT,V_STRING R$ = PopInt(Bank) Case V_FLOATCONST R$ = PopFloat(Bank) Case V_STRINGCONST R$ = PopString(Bank) Default R$ = PopInt(Bank) End Select Return R$ End Function Function PushInt(Bank,Value%) Position = BankSize(Bank) ResizeBank(Bank,Position+4) PokeInt(Bank,Position,Value) End Function Function PopInt(Bank) R = PeekInt(Bank,BankSize(Bank)-4) ResizeBank(Bank,BankSize(Bank)-4) Return R End Function Function PushByte(Bank,Value%) Position = BankSize(Bank) ResizeBank(Bank,Position+1) PokeByte(Bank,Position,Value) End Function Function PopByte(Bank) P = BankSize(Bank) R = PeekByte(Bank,BankSize(Bank)-1) ResizeBank(Bank,BankSize(Bank)-1) Return R End Function Function PushFloat(Bank,Value#) Position = BankSize(Bank) ResizeBank(Bank,Position+4) PokeFloat(Bank,Position,Value) Return Position/4 End Function Function PopFloat#(Bank) R# = PeekFloat(Bank,BankSize(Bank)-4) PokeInt(Bank,BankSize(Bank)-4,0) ResizeBank(Bank,BankSize(Bank)-4) Return R# End Function Function PushString(Bank,Text$) Position% = BankSize(Bank) ResizeBank(Bank,BankSize(Bank)+(Len(Text$)+1)) PokeByte(Bank,Position,0) For n = 1 To Len(Text$) PokeByte(Bank,Position+n,Asc(Mid(Text$,n,1))) Next Return Position/4 End Function Function PopString$(Bank) n = BankSize(Bank)-1 Repeat K = PeekByte(Bank,n) If K > 0 Then Text$ = Chr(K)+Text$ n = n - 1 Until K = 0 ResizeBank(Bank,n+1) Return Text$ End Function ;; Does not work if you've pushed a string onto the bank Function Objects(Bank) Return BankSize(Bank)/5 End Function Function PokeString(Bank,Offset,Text$) For n = 1 To Len(Text$) PokeByte Bank,Offset+(n-1),Asc(Mid(Text$,n,1)) Next PokeByte Bank,Offset+n,0 End Function Function PeekString$(Bank,Offset) For n = 0 To BankSize(Bank)-1 A = PeekByte(Bank,Offset+n) If A = 0 Then Exit Value$ = Value$ + Chr(A) Next Return Value$ End Function ;#End Region ;#Region VARIABLE Type Variable Field Name$ Field Value$ Field Class% End Type ;; Datatype bytecodes Const V_FLOAT = %00000001 Const V_INT = %00000010 Const V_STRING = %00000100 Const V_FLOATCONST = %00001000 Const V_INTCONST = %00010000 Const V_STRINGCONST = %00100000 Function Variable$(Name$,Class%,Value$) Name$ = Lower(Trim(Name$)) For v.Variable = Each Variable If v\Name = Name$ Then Return Handle(v) Next v.Variable = New Variable v\Name = Name v\Class = Class v\Value = Value Return Handle(v) End Function Function VarName$(ID) v.Variable = Object.Variable(ID) Return v\Name End Function Function VarValue$(ID) v.Variable = Object.Variable(ID) Return v\Value End Function Function VarClass%(ID) v.Variable = Object.Variable(ID) Return v\Class End Function Function GetVariable$(ID$,Class%) Select Class Case V_FLOATCONST,V_INTCONST,V_STRINGCONST Return ID$ Case V_FLOAT,V_INT,V_STRING For v.Variable = Each Variable If Handle(v) = ID Then Return v\Value EndIf Next End Select Return ID$ End Function ;#End Region ;#Region TOKENS Const T_CALL = %11001100 ;; Binary because it's a bytecode and not a token Const T_PRINT = $100000 Const T_LET = $100001 Const T_ADD = $100002 Const T_SUB = $100003 Const T_MUL = $100004 Const T_DIV = $100005 Const T_MOD = $100006 Const T_STRING = $100007 Const T_PLOT = $100008 ;#End Region ;; Stacks... Global CallStack = CreateBank(0) Global VarStack = CreateBank(0) Global InStack = CreateBank(0) MyFloat = Variable("weeblebob",V_FLOAT,3.23456) ;; If I had a function to parse the files, I could process the script and push opcodes onto the callstack. ;; That said, this is the example 'script'. A simple script that prints a number multiplied by a variable PushObject CallStack,T_PRINT,T_CALL PushObject CallStack,MyFloat,V_FLOAT PushObject CallStack,T_MUL,T_CALL PushObject CallStack,10,V_INTCONST ;; Use the Plot function to create a rect... For y = 0 To 24 For x = 0 To 24 PushObject CallStack,T_PLOT,T_CALL PushObject CallStack,25+x,V_INTCONST PushObject CallStack,25+y,V_INTCONST Next Next S = BankSize(CallStack) ;; Problem here is that this is done purely on a step-by-step basis instead of doing multiplication and division first T = MilliSecs() While Objects(CallStack) > 0 Inst$ = PopObject(CallStack) Class% = PopByte(PoppedClass) Select Class Case T_CALL Select Int(Inst) Case T_ADD Var$ = PopObject(VarStack) Byte = PopByte(PoppedClass) VarB$ = PopObject(CallStack) ByteB = PopByte(PoppedClass) Var$ = GetVariable(Var$,Byte) VarB$ = GetVariable(VarB$,ByteB) PushObject VarStack,Float(VarB) + Float(Var),V_FLOATCONST Case T_MUL Var$ = PopObject(VarStack) Byte = PopByte(PoppedClass) VarB$ = PopObject(CallStack) ByteB = PopByte(PoppedClass) Var$ = GetVariable(Var$,Byte) VarB$ = GetVariable(VarB$,ByteB) PushObject VarStack,Float(VarB) * Float(Var),V_FLOATCONST Case T_DIV Var$ = PopObject(VarStack) Byte = PopByte(PoppedClass) VarB$ = PopObject(CallStack) ByteB = PopByte(PoppedClass) Var$ = GetVariable(Var$,Byte) VarB$ = GetVariable(VarB$,ByteB) PushObject VarStack,Float(VarB) / Float(Var),V_FLOATCONST Case T_SUB Var$ = PopObject(VarStack) Byte = PopByte(PoppedClass) VarB$ = PopObject(CallStack) ByteB = PopByte(PoppedClass) Var$ = GetVariable(Var$,Byte) VarB$ = GetVariable(VarB$,ByteB) PushObject VarStack,Float(VarB) - Float(Var),V_FLOATCONST Case T_MOD Var$ = PopObject(VarStack) Byte = PopByte(PoppedClass) VarB$ = PopObject(CallStack) ByteB = PopByte(PoppedClass) Var$ = GetVariable(Var$,Byte) VarB$ = GetVariable(VarB$,ByteB) PushObject VarStack,Float(VarB) Mod Float(Var),V_FLOATCONST Case T_LET Name$ = GetVariable(PopObject(VarStack),PopByte(PoppedClass)) Value$ = PopObject(VarStack) Class% = PopByte(PoppedClass) Value$ = GetVariable(Value,Class) Variable(Name$,Value$,Class) Default PushObject InStack,Int(Inst),T_CALL End Select Default PushObject VarStack,Inst,Class End Select Wend T = MilliSecs() - T T2 = MilliSecs() While Objects(InStack) > 0 Inst = PopObject(InStack) PopByte PoppedClass Select Int(Inst) Case T_PRINT Var$ = PopObject(VarStack) Byte% = PopByte(PoppedClass) Print GetVariable(Var$,Byte) Case T_PLOT X% = GetVariable(PopObject(VarStack),PopByte(PoppedClass)) Y% = GetVariable(PopObject(VarStack),PopByte(PoppedClass)) Plot X,Y End Select Wend T2 = MilliSecs() - T2 Print "" Print "" Print "" Print "" Print "First loop took "+T Print "Second loop took "+T2 FreeBank PoppedClass FreeBank CallStack FreeBank VarStack FreeBank InStack WaitKey
No media required.
If anyone would like to try to help me to improve upon this, I'd be happy. Of course, this is another open source project of mine.