Unit MPL_Execute; {$I M_OPS.PAS} Interface Uses DOS, MPL_FileIO, BBS_Common; {$I MPL_TYPES.PAS} Const mplExecuteBuffer = 8 * 1024; Type TInterpEngine = Class Owner : Pointer; ErrStr : String; ErrNum : Byte; DataFile : PCharFile; CurVarNum : Word; CurVarID : Word; VarData : VarDataRec; Ch : Char; W : Word; IoError : LongInt; ReloadMenu : Boolean; DirInfo : SearchRec; IdxVarDir : Word; IdxVarUser : Word; IdxVarMBase : Word; IdxVarMGroup : Word; IdxVarFBase : Word; IdxVarFGroup : Word; ParamsStr : String; MPEName : String; Done : Boolean; ExitProc : Boolean; SavedMCI : Boolean; SavedGroup : Boolean; SavedArrow : Boolean; {$IFDEF LOGGING} Depth : LongInt; {$ENDIF} Function GetErrorMsg : String; Procedure Error (Err: Byte; Str: String); Procedure MoveToPos (Num: LongInt); Procedure SkipBlock; Function CurFilePos : LongInt; Procedure NextChar; Procedure NextWord; Procedure PrevChar; Function GetDataPtr (VN: Word; Var A: TArrayInfo; Var R: TRecInfo) : Pointer; Function GetDataSize (VarNum: Word) : Word; Function FindVariable (ID: Word) : Word; Procedure CheckArray (VN: Word; Var A: TArrayInfo; Var R: TRecInfo); Function GetNumber (VN: Word; Var A: TArrayInfo; Var R: TRecInfo) : Real; Function RecastNumber (Var Num; T: TIdentTypes) : Real; Function EvaluateNumber : Real; Function EvaluateString : String; Function EvaluateBoolean : Boolean; Procedure SetString (VarNum: Word; Var A: TArrayInfo; Var R: TRecInfo; Str: String); Procedure SetNumber (VN: Word; Num: Real; Var A: TArrayInfo; Var R: TRecInfo); Procedure SetVariable (VarNum: Word); Function DefineVariable : LongInt; Procedure DefineProcedure; Procedure StatementRepeatUntil; Function StatementIfThenElse : Byte; Function StatementCase : Byte; Procedure StatementForLoop; Procedure StatementWhileDo; Function ExecuteProcedure (DP: Pointer) : TIdentTypes; Function ExecuteBlock (StartVar: Word) : Byte; // BBS DATA ACCESS FUNCTIONS Procedure FileReadLine (Var F: File; Var Str: String); Procedure FileWriteLine (Var F: File; Str: String); Procedure GetUserVars (Var U: RecUser); Procedure PutUserVars (Var U: RecUser); Function GetUserRecord (Num: LongInt) : Boolean; Procedure PutUserRecord (Num: LongInt); Procedure GetMBaseVars (Var M: RecMessageBase); Function GetMBaseRecord (Num: LongInt) : Boolean; Function GetMBaseStats (Num: LongInt; Var Total, New, Yours: LongInt) : Boolean; Procedure GetMGroupVars (Var G: RecGroup); Function GetMGroupRecord (Num: LongInt) : Boolean; Procedure GetFBaseVars (Var F: RecFileBase); Function GetFBaseRecord (Num: LongInt) : Boolean; Procedure GetFGroupVars (Var G: RecGroup); Function GetFGroupRecord (Num: LongInt) : Boolean; Constructor Create (O: Pointer); Destructor Destroy; Override; Function Execute (FN: String) : Byte; {$IFDEF LOGGING} Procedure LogVarInformation (Num: LongInt); {$ENDIF} End; Function ExecuteMPL (Owner: Pointer; Str: String) : Byte; Implementation Uses m_Bits, m_Strings, m_DateTime, m_Types, m_FileIO, BBS_Core, BBS_IO, BBS_General, BBS_Ansi_MenuBox; {$I MPL_COMMON.PAS} {$IFDEF LOGGING} Procedure TInterpEngine.LogVarInformation (Num: LongInt); Begin Session.SystemLog(' DUMP VAR ' + strI2S(Num)); With VarData[Num]^ Do Begin Session.SystemLog(' ID: ' + strI2S(VarID)); Session.SystemLog(' Type: ' + strI2S(Ord(vType))); Session.SystemLog(' DataSize: ' + strI2S(DataSize)); Session.SystemLog(' VarSize: ' + strI2S(VarSize)); Session.SystemLog(' Kill: ' + strI2S(Ord(Kill))); If Data <> NIL Then Session.SystemLog(' Data: Assigned') Else Session.SystemLog(' Data: NIL'); End; // Params : Array[1..mplMaxProcParams] of Char; // NumParams : Byte; // pID : Array[1..mplMaxProcParams] of Word; // ProcPos : LongInt; // Data : PStack; // ArrPos : Byte; // ArrDims : TArrayInfo; End; {$ENDIF} Procedure TInterpEngine.GetUserVars (Var U: RecUser); Begin Move (U.PermIdx, VarData[IdxVarUser ]^.Data^, SizeOf(U.PermIdx)); Move (U.RealName, VarData[IdxVarUser + 1 ]^.Data^, SizeOf(U.RealName)); Move (U.Handle, VarData[IdxVarUser + 2 ]^.Data^, SizeOf(U.Handle)); Move (U.Address, VarData[IdxVarUser + 3 ]^.Data^, SizeOf(U.Address)); Move (U.Security, VarData[IdxVarUser + 4 ]^.Data^, SizeOf(U.Security)); Move (U.Gender, VarData[IdxVarUser + 5 ]^.Data^, SizeOf(U.Gender)); Move (U.FirstOn, VarData[IdxVarUser + 6 ]^.Data^, SizeOf(U.FirstOn)); Move (U.LastOn, VarData[IdxVarUser + 7 ]^.Data^, SizeOf(U.LastOn)); Move (U.DateType, VarData[IdxVarUser + 8 ]^.Data^, SizeOf(U.DateType)); Move (U.Calls, VarData[IdxVarUser + 9 ]^.Data^, SizeOf(U.Calls)); Move (U.Password, VarData[IdxVarUser + 10]^.Data^, SizeOf(U.Password)); Move (U.Flags, VarData[IdxVarUser + 11]^.Data^, SizeOf(U.Flags)); Move (U.LastFBase, VarData[IdxVarUser + 12]^.Data^, SizeOf(U.LastFBase)); Move (U.LastFGroup, VarData[IdxVarUser + 13]^.Data^, SizeOf(U.LastFGroup)); Move (U.LastMBase, VarData[IdxVarUser + 14]^.Data^, SizeOf(U.LastMBase)); Move (U.LastMGroup, VarData[IdxVarUser + 15]^.Data^, SizeOf(U.LastMGroup)); End; Procedure TInterpEngine.PutUserVars (Var U: RecUser); Begin Move (VarData[IdxVarUser ]^.Data^, U.PermIdx, SizeOf(U.PermIdx)); Move (VarData[IdxVarUser + 1 ]^.Data^, U.RealName, SizeOf(U.RealName)); Move (VarData[IdxVarUser + 2 ]^.Data^, U.Handle, SizeOf(U.Handle)); Move (VarData[IdxVarUser + 3 ]^.Data^, U.Address, SizeOf(U.Address)); Move (VarData[IdxVarUser + 4 ]^.Data^, U.Security, SizeOf(U.Security)); Move (VarData[IdxVarUser + 5 ]^.Data^, U.Gender, SizeOf(U.Gender)); Move (VarData[IdxVarUser + 6 ]^.Data^, U.FirstOn, SizeOf(U.FirstOn)); Move (VarData[IdxVarUser + 7 ]^.Data^, U.LastOn, SizeOf(U.LastOn)); Move (VarData[IdxVarUser + 8 ]^.Data^, U.DateType, SizeOf(U.DateType)); Move (VarData[IdxVarUser + 9 ]^.Data^, U.Calls, SizeOf(U.Calls)); Move (VarData[IdxVarUser + 10]^.Data^, U.Password, SizeOf(U.Password)); Move (VarData[IdxVarUser + 11]^.Data^, U.Flags, SizeOf(U.Flags)); Move (VarData[IdxVarUser + 12]^.Data^, U.LastFBase, SizeOf(U.LastFBase)); Move (VarData[IdxVarUser + 13]^.Data^, U.LastFGroup, SizeOf(U.LastFGroup)); Move (VarData[IdxVarUser + 14]^.Data^, U.LastMBase, SizeOf(U.LastMBase)); Move (VarData[IdxVarUser + 15]^.Data^, U.LastMGroup, SizeOf(U.LastMGroup)); End; Function TInterpEngine.GetUserRecord (Num: LongInt) : Boolean; Var F : File; U : RecUser; Begin Result := False; Assign (F, Config.DataPath + 'users.dat'); If Not ioReset(F, SizeOf(RecUser), fmRWDN) Then Exit; If ioSeek(F, Pred(Num)) And (ioRead(F, U)) Then Begin GetUserVars(U); Result := True; End; Close (F); End; Procedure TInterpEngine.PutUserRecord (Num: LongInt); Var F : File; U : RecUser; Begin Assign (F, Config.DataPath + 'users.dat'); If Not ioReset(F, SizeOf(RecUser), fmRWDN) Then Exit; PutUserVars(U); If Not ioSeek(F, Pred(Num)) Then Begin Close(F); Exit; End; IoWrite (F, U); Close (F); End; Procedure TInterpEngine.GetMBaseVars (Var M: RecMessageBase); Begin Move (M.Index, VarData[IdxVarMBase ]^.Data^, SizeOf(M.Index)); Move (M.Name, VarData[IdxVarMBase + 1 ]^.Data^, SizeOf(M.Name)); Move (M.ListACS, VarData[IdxVarMBase + 2 ]^.Data^, SizeOf(M.ListACS)); Move (M.ReadACS, VarData[IdxVarMBase + 3 ]^.Data^, SizeOf(M.ReadACS)); Move (M.PostACS, VarData[IdxVarMBase + 4 ]^.Data^, SizeOf(M.PostACS)); Move (M.SysopACS, VarData[IdxVarMBase + 5 ]^.Data^, SizeOf(M.SysopACS)); End; Function TInterpEngine.GetMBaseStats (Num: LongInt; Var Total, New, Yours: LongInt) : Boolean; Var M : RecMessageBase; Begin Result := Session.Msgs.GetRecord(Num, M); If Result Then Session.Msgs.GetMessageStats(M, Total, New, Yours); End; Function TInterpEngine.GetMBaseRecord (Num: LongInt) : Boolean; Var M : RecMessageBase; Begin Result := Session.Msgs.GetRecord(Num, M); If Result Then GetMBaseVars(M); End; Procedure TInterpEngine.GetMGroupVars (Var G: RecGroup); Begin Move (G.Name, VarData[IdxVarMGroup ]^.Data^, SizeOf(G.Name)); Move (G.ACS, VarData[IdxVarMGroup + 1 ]^.Data^, SizeOf(G.ACS)); Move (G.Hidden, VarData[IdxVarMGroup + 2 ]^.Data^, SizeOf(G.Hidden)); End; Function TInterpEngine.GetMGroupRecord (Num: LongInt) : Boolean; Var F : File; G : RecGroup; Begin Result := False; Assign (F, Config.DataPath + 'groups_g.dat'); If Not ioReset(F, SizeOf(RecGroup), fmRWDN) Then Exit; If ioSeek(F, Pred(Num)) And (ioRead(F, G)) Then Begin GetMGroupVars(G); Result := True; End; Close (F); End; Procedure TInterpEngine.GetFBaseVars (Var F: RecFileBase); Begin Move (F.Name, VarData[IdxVarFBase ]^.Data^, SizeOf(F.Name)); Move (F.ListACS, VarData[IdxVarFBase + 1 ]^.Data^, SizeOf(F.ListACS)); End; Function TInterpEngine.GetFBaseRecord (Num: LongInt) : Boolean; Var F : File; FB : RecFileBase; Begin Result := False; Assign (F, Config.DataPath + 'fbases.dat'); If Not ioReset(F, SizeOf(RecFileBase), fmRWDN) Then Exit; If ioSeek(F, Pred(Num)) And (ioRead(F, FB)) Then Begin GetFBaseVars(FB); Result := True; End; Close (F); End; Procedure TInterpEngine.GetFGroupVars (Var G: RecGroup); Begin Move (G.Name, VarData[IdxVarFGroup ]^.Data^, SizeOf(G.Name)); Move (G.ACS, VarData[IdxVarFGroup + 1 ]^.Data^, SizeOf(G.ACS)); Move (G.Hidden, VarData[IdxVarFGroup + 2 ]^.Data^, SizeOf(G.Hidden)); End; Function TInterpEngine.GetFGroupRecord (Num: LongInt) : Boolean; Var F : File; G : RecGroup; Begin Result := False; Assign (F, Config.DataPath + 'groups_f.dat'); If Not ioReset(F, SizeOf(RecGroup), fmRWDN) Then Exit; If ioSeek(F, Pred(Num)) And (ioRead(F, G)) Then Begin GetFGroupVars(G); Result := True; End; Close (F); End; Constructor TInterpEngine.Create (O: Pointer); Begin Inherited Create; Owner := O; ErrNum := 0; ErrStr := ''; Ch := #0; W := 0; {$IFDEF LOGGING} Depth := 0; {$ENDIF} End; Destructor TInterpEngine.Destroy; Var Count : LongInt; Begin For Count := 1 to CurVarNum Do Begin If (VarData[Count]^.Kill) And (VarData[Count]^.Data <> NIL) Then FreeMem(VarData[Count]^.Data, VarData[Count]^.DataSize); Dispose(VarData[Count]); End; CurVarNum := 0; Inherited Destroy; End; Function TInterpEngine.GetErrorMsg : String; Begin Result := ''; Case ErrNum of mpxEndOfFile : Result := 'Unexpected end of file'; mpxInvalidFile : Result := 'Invalid executable: ' + ErrStr; mpxVerMismatch : Result := 'Version mismatch: ' + ErrStr + ' / ' + mplVersion; mpxUnknownOp : Result := 'Unknown Token: ' + ErrStr; mpxMultiInit : Result := 'Unable to initialize variable'; mpxDivisionByZero : Result := 'Division by zero'; mpxMathematical : Result := 'Parsing error'; End; End; Procedure TInterpEngine.Error (Err: Byte; Str: String); Begin If ErrNum > 0 Then Exit; ErrNum := Err; ErrStr := Str; End; Procedure TInterpEngine.MoveToPos (Num: LongInt); Begin DataFile^.Seek (Num + mplVerLength); End; Function TInterpEngine.CurFilePos : LongInt; Begin Result := DataFile^.FilePos - mplVerLength; End; Procedure TInterpEngine.NextChar; Begin Ch := DataFile^.Read; End; Procedure TInterpEngine.NextWord; Var Res : LongInt; Begin DataFile^.BlockRead (W, 2, Res); End; Procedure TInterpEngine.PrevChar; Begin MoveToPos (CurFilePos - 1); End; Function TInterpEngine.FindVariable (ID: Word) : Word; Var Count : LongInt; Begin Result := 0; Count := CurVarNum; If CurVarNum = 0 Then Exit; Repeat If VarData[Count]^.VarID = ID Then Begin Result := Count; Exit; End; Dec (Count); Until (Count = 0); End; Function TInterpEngine.GetDataPtr (VN: Word; Var A: TArrayInfo; Var R: TRecInfo) : Pointer; Begin With VarData[VN]^ Do Case ArrPos of 0 : Result := @Data^[R.Offset + 1]; 1 : Result := @Data^[VarSize * (A[1] - 1) + 1 + R.Offset]; 2 : Result := @Data^[VarSize * ((A[1] - 1) * ArrDim[2] + A[2]) + R.Offset]; 3 : Result := @Data^[VarSize * ((A[1] - 1) * (ArrDim[2] * ArrDim[3]) + (A[2] - 1) * ArrDim[3] + A[3]) + R.Offset]; End; End; Procedure TInterpEngine.CheckArray (VN: Word; Var A: TArrayInfo; Var R: TRecInfo); Var Count : Word; Temp : TArrayInfo; Offset : Word; Begin For Count := 1 to mplMaxArrayDem Do A[Count] := 1; R.Offset := 0; R.vType := VarData[VN]^.vType; R.OneSize := VarData[VN]^.VarSize; If VarData[VN]^.ArrPos > 0 Then Begin For Count := 1 to VarData[VN]^.ArrPos Do A[Count] := Trunc(EvaluateNumber); End; If VarData[VN]^.vType = iRecord Then Begin // blockread this crap instead of this? NextChar; R.vType := Char2VarType(Ch); NextWord; R.OneSize := W; NextWord; R.Offset := W; NextWord; R.ArrDem := W; If R.ArrDem > 0 Then Begin For Count := 1 to R.ArrDem Do Temp[Count] := Trunc(EvaluateNumber); Offset := 0; For Count := 1 to R.ArrDem Do Offset := Offset + ((Temp[Count] - 1) * R.OneSize); R.Offset := R.Offset + Offset; End; End; End; Function TInterpEngine.GetNumber (VN: Word; Var A: TArrayInfo; Var R: TRecInfo) : Real; Begin Case R.vType of iByte : Result := Byte(GetDataPtr(VN, A, R)^); iShort : Result := ShortInt(GetDataPtr(VN, A, R)^); iWord : Result := Word(GetDataPtr(VN, A, R)^); iInteger : Result := Integer(GetDataPtr(VN, A, R)^); iLongInt : Result := LongInt(GetDataPtr(VN, A, R)^); iCardinal : Result := Cardinal(GetDataPtr(VN, A, R)^); iReal : Result := Real(GetDataPtr(VN, A, R)^); End; End; Function TInterpEngine.RecastNumber (Var Num; T: TIdentTypes) : Real; Begin Case T of iByte : Result := Byte(Num); iShort : Result := ShortInt(Num); iWord : Result := Word(Num); iInteger : Result := Integer(Num); iLongInt : Result := LongInt(Num); iCardinal : Result := Cardinal(Num); iReal : Result := Real(Num); End; End; Function TInterpEngine.EvaluateNumber : Real; Var CheckChar : Char; VarNum : Word; PowerRes : Real; Procedure ParseNext; Begin NextChar; If Ch = Char(opCloseNum) Then CheckChar := ^M Else CheckChar := Ch; End; Function AddSubtract : Real; Var OpChar : Char; Function MultiplyDivide : Real; Var OpChar : Char; Function Power : Real; Function SignedOp : Real; Function UnsignedOp : Real; Var Start : LongInt; ArrayInfo : TArrayInfo; RecInfo : TRecInfo; NumStr : String; Begin Case TTokenOpsRec(Byte(CheckChar)) of opLeftParan : Begin ParseNext; Result := AddSubtract; ParseNext; End; opVariable : Begin NextWord; VarNum := FindVariable(W); CheckArray (VarNum, ArrayInfo, RecInfo); Result := GetNumber(VarNum, ArrayInfo, RecInfo); ParseNext; End; opProcExec : Begin Result := RecastNumber(Result, ExecuteProcedure(@Result)); ParseNext; End; Else NumStr := ''; Repeat NumStr := NumStr + CheckChar; ParseNext; Until Not (CheckChar in ['0'..'9', '.', 'E']); Val(NumStr, Result, Start); End; End; Begin If CheckChar = '-' Then Begin ParseNext; Result := -UnsignedOp; End Else Result := UnsignedOp; End; Begin Result := SignedOp; While CheckChar = '^' Do Begin ParseNext; If Result <> 0 Then Result := Exp(Ln(Abs(Result)) * SignedOp) Else Result := 0; End; End; Begin Result := Power; While CheckChar in ['%','*','/'] Do Begin OpChar := CheckChar; ParseNext; Case OpChar of '%' : Result := Trunc(Result) MOD Trunc(Power); '*' : Result := Result * Power; '/' : Begin PowerRes := Power; If PowerRes = 0 Then Error (mpxDivisionByZero, '') Else Result := Result / PowerRes; End; End; End; End; Begin Result := MultiplyDivide; While CheckChar in ['+','-','&','|','@','<','>'] Do Begin OpChar := CheckChar; ParseNext; Case OpChar of '+' : Result := Result + MultiplyDivide; '-' : Result := Result - MultiplyDivide; '&' : Result := Trunc(Result) AND Trunc(MultiplyDivide); '|' : Result := Trunc(Result) OR Trunc(MultiplyDivide); '@' : Result := Trunc(Result) XOR Trunc(MultiplyDivide); '<' : Result := Trunc(Result) SHL Trunc(MultiplyDivide); '>' : Result := Trunc(Result) SHR Trunc(MultiplyDivide); End; End; End; Begin NextChar; ParseNext; Result := AddSubtract; End; Function TInterpEngine.EvaluateString : String; Var VarNum : Word; ArrayData : TArrayInfo; RecInfo : TRecInfo; Res : LongInt; Begin Result := ''; NextChar; Case TTokenOpsRec(Byte(Ch)) of opVariable : Begin NextWord; VarNum := FindVariable(W); CheckArray (VarNum, ArrayData, RecInfo); If RecInfo.vType = iChar Then Begin Result[0] := #1; Result[1] := Char(GetDataPtr(VarNum, ArrayData, RecInfo)^); End Else Result := String(GetDataPtr(VarNum, ArrayData, RecInfo)^); End; opOpenString : Begin NextChar; Result[0] := Ch; DataFile^.BlockRead (Result[1], Byte(Ch), Res); End; opProcExec : Case ExecuteProcedure(@Result) of iChar : Begin // convert to string if its a char Result[1] := Result[0]; Result[0] := #1; End; End; End; NextChar; If Ch = Char(opStrArray) Then Begin Result := Result[Trunc(EvaluateNumber)]; NextChar; End; If Ch = Char(opStrAdd) Then Result := Result + EvaluateString Else PrevChar; End; Function TInterpEngine.EvaluateBoolean : Boolean; Type tOp = ( tOpNone, tOpEqual, tOpNotEqual, tOpGreater, tOpLess, tOpEqGreat, tOpEqLess ); Var VarNum : Word; VarType1 : TIdentTypes; VarType2 : TIdentTypes; OpType : tOp; GotA : Boolean; GotB : Boolean; BooleanA : Boolean; BooleanB : Boolean; IsNot : Boolean; RealA : Real; RealB : Real; StringA : String; StringB : String; ArrayData : TArrayInfo; RecInfo : TRecInfo; Begin // set default result? VarType1 := iNone; VarType2 := iNone; GotA := False; GotB := False; OpType := tOpNone; IsNot := False; Repeat NextChar; // put these in numerical order... Case TTokenOpsRec(Byte(Ch)) of opLeftParan : Begin BooleanA := EvaluateBoolean; VarType1 := iBool; GotA := True; NextChar; End; opVariable : Begin NextWord; VarNum := FindVariable(W); CheckArray(VarNum, ArrayData, RecInfo); VarType1 := RecInfo.vType; If VarType1 = iBool Then BooleanA := ByteBool(GetDataPtr(VarNum, ArrayData, RecInfo)^) Else If (VarType1 in vStrings) Then Begin NextChar; If Ch = Char(opStrArray) Then StringA := String(GetDataPtr(VarNum, ArrayData, RecInfo)^)[Trunc(EvaluateNumber)] Else Begin PrevChar; If VarType1 = iChar Then Begin StringA[0] := #1; StringA[1] := Char(GetDataPtr(VarNum, ArrayData, RecInfo)^); End Else StringA := String(GetDataPtr(VarNum, ArrayData, RecInfo)^); End; End Else If VarType1 in vNums Then RealA := GetNumber(VarNum, ArrayData, RecInfo); // evalnumber here GotA := True; End; opProcExec : Begin VarType1 := ExecuteProcedure(@StringA); If VarType1 = iBool Then BooleanA := Boolean(Byte(StringA[0])) else If VarType1 in vNums Then RealA := RecastNumber(StringA, VarType1) else if VarType1 = iChar Then Begin StringA[1] := StringA[0]; StringA[0] := #1; End; GotA := True; End; opTrue : Begin // we can combine true/false here... BooleanA := True; VarType1 := iBool; GotA := True; End; opFalse : Begin BooleanA := False; VarType1 := iBool; GotA := True; End; opOpenString : Begin PrevChar; StringA := EvaluateString; VarType1 := iString; GotA := True; End; opOpenNum : Begin PrevChar; RealA := EvaluateNumber; VarType1 := iReal; GotA := True; End; opNot : IsNot := Not IsNot; End; Until (ErrNum <> 0) or GotA; If ErrNum <> 0 Then Exit; NextChar; // we shouldnt even need this... just use the actual tokens...??? Case TTokenOpsRec(Byte(Ch)) of opEqual : OpType := tOpEqual; opNotEqual : OpType := tOpNotEqual; opGreater : OpType := tOpGreater; opLess : OpType := tOpLess; opEqGreat : OpType := tOpEqGreat; opEqLess : OpType := tOpEqLess; Else Result := BooleanA; PrevChar; End; If OpType <> tOpNone Then Begin Repeat NextChar; Case TTokenOpsRec(Byte(Ch)) of opLeftParan : Begin BooleanB := EvaluateBoolean; VarType2 := iBool; GotB := True; NextChar; End; opVariable : Begin NextWord; VarNum := FindVariable(W); CheckArray (VarNum, ArrayData, RecInfo); VarType2 := RecInfo.vType; If VarType2 = iBool Then BooleanB := ByteBool(GetDataPtr(VarNum,ArrayData, RecInfo)^) Else If (VarType2 in vStrings) Then Begin NextChar; If Ch = Char(opStrArray) Then StringB := String(GetDataPtr(VarNum, ArrayData, RecInfo)^)[Trunc(EvaluateNumber)] Else Begin PrevChar; If VarType2 = iChar Then Begin StringB[0] := #1; StringB[1] := Char(GetDataPtr(VarNum, ArrayData, RecInfo)^); End Else StringB := String(GetDataPtr(VarNum, ArrayData, RecInfo)^); End; End Else If VarType2 in vNums Then RealB := GetNumber(VarNum, ArrayData, RecInfo); GotB := True; End; opProcExec : Begin VarType2 := ExecuteProcedure(@StringB); If VarType2 = iBool Then BooleanB := Boolean(Byte(StringB[0])) Else If VarType2 in vNums Then RealB := RecastNumber(StringB, VarType2) Else if VarType2 = iChar Then Begin StringB[1] := StringB[0]; StringB[0] := #1; End; GotB := True; End; opTrue : Begin BooleanB := True; VarType2 := iBool; GotB := True; End; opFalse : Begin BooleanB := False; VarType2 := iBool; GotB := True; End; opOpenString : Begin PrevChar; StringB := EvaluateString; VarType2 := iString; GotB := True; End; opOpenNum : Begin PrevChar; RealB := EvaluateNumber; VarType2 := iReal; GotB := True; End; End; Until (ErrNum <> 0) or GotB; If ErrNum <> 0 Then Exit; Result := False; Case OpType of tOpEqual : If (VarType1 in vStrings) Then Result := StringA = StringB Else If VarType1 = iBool Then Result := BooleanA = BooleanB Else Result := RealA = RealB; tOpNotEqual : If (VarType1 in vStrings) Then Result := StringA <> StringB Else If VarType1 = iBool Then Result := BooleanA <> BooleanB Else Result := RealA <> RealB; tOpGreater : If (VarType1 in vStrings) Then Result := StringA > StringB Else If VarType1 = iBool Then Result := BooleanA > BooleanB Else Result := RealA > RealB; tOpLess : If (VarType1 in vStrings) Then Result := StringA < StringB Else If VarType1 = iBool Then Result := BooleanA < BooleanB Else Result := RealA < RealB; tOpEqGreat : If (VarType1 in vStrings) Then Result := StringA >= StringB Else If VarType1 = iBool Then Result := BooleanA >= BooleanB Else Result := RealA >= RealB; tOpEqLess : If (VarType1 in vStrings) Then Result := StringA <= StringB Else If VarType1 = iBool Then Result := BooleanA <= BooleanB Else Result := RealA <= RealB; End; End; If IsNot Then Result := Not Result; NextChar; Case TTokenOpsRec(Byte(Ch)) of opAnd : Result := EvaluateBoolean And Result; opOr : Result := EvaluateBoolean Or Result; Else PrevChar; End; End; Procedure TInterpEngine.SetString (VarNum: Word; Var A: TArrayInfo; Var R: TRecInfo; Str: String); Begin If R.vType = iString Then Begin If Ord(Str[0]) >= R.OneSize Then Str[0] := Chr(R.OneSize - 1); Move (Str, GetDataPtr(VarNum, A, R)^, R.OneSize); End Else Move (Str[1], GetDataPtr(VarNum, A, R)^, 1); End; Procedure TInterpEngine.SetVariable (VarNum: Word); Var ArrayData : TArrayInfo; RecInfo : TRecInfo; Target : Byte; TempStr : String; RecID : Word; AD : TArrayInfo; RI : TRecInfo; Begin CheckArray (VarNum, ArrayData, RecInfo); Case RecInfo.vType of iChar, iString : Begin NextChar; If Ch = Char(opStrArray) Then Begin TempStr := String(GetDataPtr(VarNum, ArrayData, RecInfo)^); Target := Byte(Trunc(EvaluateNumber)); TempStr[Target] := EvaluateString[1]; SetString (VarNum, ArrayData, RecInfo, TempStr); End Else Begin PrevChar; SetString (VarNum, ArrayData, RecInfo, EvaluateString); End; End; iByte : Byte(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iShort : ShortInt(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iWord : Word(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iInteger : Integer(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iLongInt : LongInt(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iCardinal : Cardinal(GetDataPtr(VarNum, ArrayData, RecInfo)^) := Trunc(EvaluateNumber); iReal : Real(GetDataPtr(VarNum, ArrayData, RecInfo)^) := EvaluateNumber; iBool : ByteBool(GetDataPtr(VarNum, ArrayData, RecInfo)^) := EvaluateBoolean; iRecord : Begin NextWord; RecID := FindVariable(W); CheckArray (RecID, AD, RI); //asdf DEBUG DEBUG // how do we get the real size of the shit here? // i added Checkarray here and ParseElement in ParseVarRecord for compiler //session.io.outfullln('datasize=' + stri2s(vardata[recid]^.datasize)); //session.io.outfullln('varsize=' + stri2s(vardata[recid]^.varsize)); //session.io.outfullln('|PN'); Move (GetDataPtr(RecID, AD, RI)^, GetDataPtr(VarNum, ArrayData, RecInfo)^, RecInfo.OneSize {VarData[RecID]^.VarSize}); // Move (VarData[RecID]^.Data^, GetDataPtr(VarNum, ArrayData, RecInfo)^, VarData[RecID]^.DataSize); End; End; End; Procedure TInterpEngine.SetNumber (VN: Word; Num: Real; Var A: TArrayInfo; Var R: TRecInfo); Begin Case R.vType of iByte : Byte(GetDataPtr(VN, A, R)^) := Trunc(Num); iShort : ShortInt(GetDataPtr(VN, A, R)^) := Trunc(Num); iWord : Word(GetDataPtr(VN, A, R)^) := Trunc(Num); iInteger : Integer(GetDataPtr(VN, A, R)^) := Trunc(Num); iLongInt : LongInt(GetDataPtr(VN, A, R)^) := Trunc(Num); iCardinal : Cardinal(GetDataPtr(VN, A, R)^) := Trunc(Num); iReal : Real(GetDataPtr(VN, A, R)^) := Num; end; end; Function TInterpEngine.GetDataSize (VarNum: Word) : Word; Var Count : Word; Begin With VarData[VarNum]^ Do Begin Result := VarSize; For Count := 1 To ArrPos Do Result := Result * ArrDim[Count]; End; End; Function TInterpEngine.DefineVariable : LongInt; Var VarType : TIdentTypes; NumVars : Word; SavedVar : Word; StrSize : Word; RecSize : Word; Count : Word; ArrayPos : Word; ArrayData : TArrayInfo; Begin Result := 0; NextChar; VarType := Char2VarType(Ch); NextChar; StrSize := 256; ArrayPos := 0; For Count := 1 To mplMaxArrayDem Do ArrayData[Count] := 1; If Ch = Char(opStrSize) Then Begin StrSize := Trunc(EvaluateNumber) + 1; NextChar; End; If Ch = Char(opTypeRec) Then Begin NextWord; RecSize := W; NextChar; End; If Ch = Char(opArrDef) Then Begin NextWord; ArrayPos := W; For Count := 1 to ArrayPos Do ArrayData[Count] := Trunc(EvaluateNumber); End; NextWord; NumVars := W; SavedVar := CurVarNum + 1; For Count := 1 to NumVars Do If ErrNum = 0 Then Begin NextWord; If FindVariable(W) > 0 Then Begin Error (mpxMultiInit, ''); Exit; End; Inc (CurVarNum); New (VarData[CurVarNum]); With VarData[CurVarNum]^ Do Begin VarID := W; vType := VarType; NumParams := 0; ProcPos := 0; ArrPos := ArrayPos; ArrDim := ArrayData; Case VarType of iString : Begin VarSize := StrSize; DataSize := GetDataSize(CurVarNum); End; iRecord : Begin VarSize := RecSize; DataSize := GetDataSize(CurVarNum); End; Else VarSize := GetVarSize(VarType); DataSize := GetDataSize(CurVarNum); End; Result := DataSize; GetMem (Data, DataSize); FillChar (Data^, DataSize, 0); Kill := True; End; End; NextChar; If Ch = Char(OpEqual) Then Begin SetVariable(SavedVar); For Count := SavedVar + 1 To CurVarNum Do Move (VarData[SavedVar]^.Data^, VarData[Count]^.Data^, VarData[SavedVar]^.DataSize); End Else PrevChar; End; Procedure TInterpEngine.FileReadLine (Var F: File; Var Str: String); Var Buf : String; BR : SmallInt; Count : Byte; SP : LongInt; Begin Str := ''; SP := FilePos(F); Count := 1; BlockRead (F, Buf[1], 255, BR); While Count <= BR Do Begin Inc (SP); If Buf[Count] = #10 Then Break; If Buf[Count] <> #13 Then Str := Str + Buf[Count]; If Count = 255 Then Begin BlockRead (F, Buf[1], 255, BR); Count := 0; End; Inc (Count); End; Seek (F, SP); IoError := IoResult; End; Procedure TInterpEngine.FileWriteLine (Var F: File; Str: String); Begin {$IFDEF WINDOWS} Str := Str + #13#10; {$ENDIF} {$IFDEF UNIX} Str := Str + #10; {$ENDIF} BlockWrite (F, Str[1], Ord(Str[0])); IoError := IoResult; End; Function TInterpEngine.ExecuteProcedure (DP: Pointer) : TIdentTypes; // okay... change this to: // array[1..mplmaxprocparams] of record // vsize : word; // vdata : pointer; // end; // VAR passing: stores dataptr to passed variable -- DONE // regular : creates var and stores its pointer into vdata -- TODO // doing this will reduce memory usage and make things even harder to // understand. // this stuff really needs to be cleaned up before records are fully // added Type TParamInfo = Array[1..mplMaxProcParams] of Record // vType : TIdentTypes; vSize : Word; //do we really nede this? can get size from vType vID : Word; vData : PStack; Case TIdentTypes of // this all needs to go... push to vData iChar : (C : Char); iString : (S : String); iByte : (B : Byte); iShort : (H : ShortInt); iWord : (W : Word); iInteger : (I : Integer); iLongInt : (L : LongInt); iCardinal : (A : Cardinal); iReal : (R : Real); iBool : (O : Boolean); End; Var VarNum : Word; Count : Word; ProcID : Word; SavedVar : Word; Param : TParamInfo; TempStr : String; TempBool : Boolean; TempByte : Byte; TempLong : LongInt; TempChar : Char; TempInt : SmallInt; Sub : LongInt; ArrayData : TArrayInfo; RecInfo : TRecInfo; Procedure Store (Var Dat; Siz: Word); Begin If DP <> NIL Then Move (Dat, DP^, Siz); End; Begin // no default result value set here NextWord; ProcID := W; VarNum := FindVariable(ProcID); For Count := 1 to VarData[VarNum]^.NumParams Do Begin With VarData[VarNum]^ Do Begin If Params[Count] = UpCase(Params[Count]) Then Begin // its a VAR type parameter, so find the variable // and directly map the data pointer to the passed vars // data pointer NextWord; Param[Count].vID := FindVariable(W); CheckArray(Param[Count].vID, ArrayData, RecInfo); Param[Count].vData := GetDataPtr(Param[Count].vID, ArrayData, RecInfo); Param[Count].vSize := VarData[Param[Count].vID]^.VarSize; // Case VarData[Param[Count].vID]^ // If VarData[Param[Count].vID]^.vType = iString Then // Param[Count].vSize := VarData[Param[Count].vID]^.VarSize; End Else Begin // this should getmem dataptr and store it there instead // will save some memory but make calling functions below a bit more // of a pain in the ass Case Params[Count] of 'c' : Begin Param[Count].vSize := 1; Param[Count].C := EvaluateString[1]; End; 's' : Begin Param[Count].vSize := 256; Param[Count].S := EvaluateString; End; 'b' : Param[Count].B := Trunc(EvaluateNumber); 'h' : Param[Count].H := Trunc(EvaluateNumber); 'w' : Param[Count].W := Trunc(EvaluateNumber); 'i' : Param[Count].I := Trunc(EvaluateNumber); 'l' : Param[Count].L := Trunc(EvaluateNumber); 'r' : Param[Count].R := EvaluateNumber; 'o' : Param[Count].O := EvaluateBoolean; 'x' : Begin NextWord; // Var ID; Param[Count].vID := FindVariable(W); Param[Count].vSize := VarData[Param[Count].vID]^.DataSize; Param[Count].vData := VarData[Param[Count].vID]^.Data; //parsesometrihng CheckArray (Param[Count].vID, ArrayData, RecInfo); End; End; End; NextChar; End; End; Result := VarData[VarNum]^.vType; // this means that its a physical procedure and not a variable // or a predefined procedure from mpl_common. If VarData[VarNum]^.ProcPos > 0 Then Begin {$IFDEF LOGGING} Session.SystemLog(' Custom Proc: ' + strI2S(ProcID)); {$ENDIF} Sub := CurFilePos; SavedVar := CurVarNum; MoveToPos(VarData[VarNum]^.ProcPos); For Count := 1 to VarData[VarNum]^.NumParams Do Begin Inc (CurVarNum); New (VarData[CurVarNum]); With VarData[CurVarNum]^ Do Begin VarID := VarData[VarNum]^.pID[Count]; vType := Char2VarType(VarData[VarNum]^.Params[Count]); NumParams := 0; ProcPos := 0; ArrPos := 0; If vType = iString Then VarSize := Param[Count].vSize Else VarSize := GetVarSize(vType); DataSize := GetDataSize(CurVarNum); If VarData[VarNum]^.Params[Count] = UpCase(VarData[VarNum]^.Params[Count]) Then Begin Data := Param[Count].vData; Kill := False; End Else Begin GetMem (Data, DataSize); Case VarData[VarNum]^.Params[Count] of 'c' : Char(Pointer(Data)^) := Param[Count].C; 's' : Begin If Ord(Param[Count].S[0]) >= VarSize Then Param[Count].S[0] := Chr(VarSize - 1); Move (Param[Count].S, Data^, VarSize); End; 'b' : Byte(Pointer(Data)^) := Param[Count].B; 'h' : ShortInt(Pointer(Data)^) := Param[Count].H; 'w' : Word(Pointer(Data)^) := Param[Count].W; 'i' : Integer(Pointer(Data)^) := Param[Count].I; 'l' : LongInt(Pointer(Data)^) := Param[Count].L; 'r' : Real(Pointer(Data)^) := Param[Count].R; 'o' : Boolean(Pointer(Data)^) := Param[Count].O; End; Kill := True; End; End; End; If VarData[VarNum]^.vType <> iNone Then Begin VarData[VarNum]^.DataSize := GetDataSize(VarNum); VarData[VarNum]^.Kill := False; GetMem (VarData[VarNum]^.Data, VarData[VarNum]^.DataSize); FillChar (VarData[VarNum]^.Data^, VarData[VarNum]^.DataSize, 0); End; ExecuteBlock (SavedVar); If ExitProc Then Begin ExitProc := False; Done := False; End; If VarData[VarNum]^.vType <> iNone Then Begin If DP <> NIL Then // force char into a string for DP if VarData[VarNum]^.vType = iChar Then Begin TempStr[0] := #1; TempStr[1] := Char(Pointer(VarData[VarNum]^.Data)^); Move (TempStr, DP^, 2); End Else Move (VarData[VarNum]^.Data^, DP^, VarData[VarNum]^.DataSize); FreeMem(VarData[VarNum]^.Data, VarData[VarNum]^.DataSize); VarData[VarNum]^.DataSize := 0; End; MoveToPos(Sub); Exit; End; // end of custom procedure execution // its not a custom procedure, its a build in proc so lets do it // this means that all of this param stuff will have to be redone // if we change it to a dataptr. what effect will this have on // execution speed? {$IFDEF LOGGING} Session.SystemLog(' Internal Proc: ' + strI2S(ProcID)); {$ENDIF} Case ProcID of 0 : Session.io.OutFull(Param[1].S); 1 : Session.io.OutFullLn(Param[1].S); 2 : Session.io.AnsiClear; 3 : Session.io.AnsiClrEOL; 4 : Session.io.AnsiGotoXY(Param[1].B, Param[2].B); 5 : Begin TempByte := Screen.CursorX; Store(TempByte, 1); End; 6 : Begin TempByte := Screen.CursorY; Store(TempByte, 1); End; 7 : Begin TempStr := Session.io.GetKey; Store(TempStr, 256); End; 8 : Begin Session.io.BufFlush; WaitMS(Param[1].L); End; 9 : Begin TempLong := Random(Param[1].L); Store (TempLong, 4); End; 10 : Begin TempChar := Chr(Param[1].B); Store (TempChar, 1); End; 11 : Begin TempByte := Ord(Param[1].S[1]); Store (TempByte, 1); End; 12 : Begin TempStr := Copy(Param[1].S, Param[2].L, Param[3].L); Store (TempStr, 256); End; 13 : Delete(String(Pointer(Param[1].vData)^), Param[2].L, Param[3].L); 14 : Insert(Param[1].S, String(Pointer(Param[2].vData)^), Param[3].L); 15 : Begin TempLong := Length(Param[1].S); Store (TempLong, 4); End; 16 : Begin TempBool := Odd(Param[1].L); Store (TempBool, 1); End; 17 : Begin TempLong := Pos(Param[1].S, Param[2].S); Store (TempLong, 4); End; 18 : Begin {$IFDEF UNIX} TempBool := Input.KeyPressed; {$ELSE} TempBool := Input.KeyPressed OR Session.Client.DataWaiting; {$ENDIF} Store (TempBool, 1); Session.io.BufFlush; End; 19 : Begin TempStr := strPadR(Param[1].S, Param[2].B, Param[3].S[1]); Store (TempStr, 256); End; 20 : Begin TempStr := strPadL(Param[1].S, Param[2].B, Param[3].S[1]); Store (TempStr, 256); End; 21 : Begin TempStr := strPadC(Param[1].S, Param[2].B, Param[3].S[1]); Store (TempStr, 256); End; 22 : Begin TempStr := strUpper(Param[1].S); Store (TempStr, 256); End; 23 : Begin TempStr := strLower(Param[1].S); Store (TempStr, 256); End; 24 : Begin TempStr := strRep(Param[1].S[1], Param[2].B); Store (TempStr, 256); End; 25 : Begin TempStr := strComma(Param[1].L); Store (TempStr, 256); End; 26 : Begin TempStr := strI2S(Param[1].L); Store (TempStr, 256); End; 27 : Begin TempLong := strS2I(Param[1].S); Store (TempLong, 4); End; 28 : Begin TempStr := strI2H(Param[1].L); Store (TempStr, 256); End; 29 : Begin TempStr := strWordGet(Param[1].B, Param[2].S, Param[3].S[1]); Store (TempStr, 256); End; 30 : Begin TempByte := strWordPos(Param[1].B, Param[2].S, Param[3].S[1]); Store (TempByte, 1); End; 31 : Begin TempByte := strWordCount(Param[1].S, Param[2].S[1]); Store (TempByte, 1); End; 32 : Begin TempStr := strStripL(Param[1].S, Param[2].S[1]); Store (TempStr, 256); End; 33 : Begin TempStr := strStripR(Param[1].S, Param[2].S[1]); Store (TempStr, 256); End; 34 : Begin TempStr := strStripB(Param[1].S, Param[2].S[1]); Store (TempStr, 256); End; 35 : Begin TempStr := strStripLow(Param[1].S); Store (TempStr, 256); End; 36 : Begin TempStr := strStripMCI(Param[1].S); Store (TempStr, 256); End; 37 : Begin TempByte := strMCILen(Param[1].S); Store (TempByte, 1); End; 38 : Begin TempStr := strInitials(Param[1].S); Store (TempStr, 256); End; 39 : Begin TempByte := strWrap(String(Pointer(Param[1].vData)^), String(Pointer(Param[2].vData)^), Param[3].B); Store (TempByte, 1); End; 40 : Begin TempStr := strReplace(Param[1].S, Param[2].S, Param[3].S); Store (TempStr, 256); End; 41 : Begin TempStr := GetEnv(Param[1].S); Store (TempStr, 256); End; 42 : Begin TempBool := FileExist(Param[1].S); Store (TempBool, 1); End; 43 : FileErase(Param[1].S); 44 : Begin TempBool := DirExists(Param[1].S); Store (TempBool, 1); End; 45 : Begin TempLong := TimerMinutes; Store (TempLong, 4); End; 46 : Begin TempLong := TimerSeconds; Store (TempLong, 4); End; 47 : Begin TempLong := CurDateDos; Store (TempLong, 4); End; 48 : Begin TempLong := CurDateJulian; Store (TempLong, 4); End; 49 : Begin TempStr := DateDos2Str(Param[1].L, Param[2].B); Store (TempStr, 256); End; 50 : Begin TempStr := DateJulian2Str(Param[1].L, Param[2].B); Store (TempStr, 256); End; 51 : Begin TempLong := DateStr2Dos(Param[1].S); Store (TempLong, 4); End; 52 : Begin TempLong := DateStr2Julian(Param[1].S); Store (TempLong, 4); End; 53 : DateG2J(Param[1].L, Param[2].L, Param[3].L, LongInt(VarData[Param[4].vID]^.Data)); 54 : DateJ2G(Param[1].L, SmallInt(Pointer(Param[2].vData)^), SmallInt(Pointer(Param[3].vData)^), SmallInt(Pointer(Param[4].vData)^)); 55 : Begin TempBool := DateValid(Param[1].S); Store (TempBool, 1); End; 56 : Begin TempStr := TimeDos2Str(Param[1].L, Param[2].O); Store (TempStr, 256); End; 57 : Begin TempByte := DayOfWeek; Store (TempByte, 1); End; 58 : Begin TempLong := DaysAgo(Param[1].L); Store (TempLong, 4); End; 59 : Begin TempStr := JustFile(Param[1].S); Store (TempStr, 256); End; 60 : Begin TempStr := JustFileName(Param[1].S); Store (TempStr, 256); End; 61 : Begin TempStr := JustFileExt(Param[1].S); Store (TempStr, 256); End; 62 : Begin Assign (File(Pointer(Param[1].vData)^), Param[2].S); FileMode := Param[3].L; End; 63 : Begin Reset (File(Pointer(Param[1].vData)^), 1); IoError := IoResult; End; 64 : Begin ReWrite (File(Pointer(Param[1].vData)^), 1); IoError := IoResult; End; 65 : Begin Close (File(Pointer(Param[1].vData)^)); IoError := IoResult; End; 66 : Begin Seek (File(Pointer(Param[1].vData)^), Param[2].L); IoError := IoResult; End; 67 : Begin TempBool := Eof(File(Pointer(Param[1].vData)^)); IoError := IoResult; Store (TempBool, 1); End; 68 : Begin TempLong := FileSize(File(Pointer(Param[1].vData)^)); IoError := IoResult; Store (TempLong, 4); End; 69 : Begin TempLong := FilePos(File(Pointer(Param[1].vData)^)); IoError := IoResult; Store (TempLong, 4); End; 70 : Begin BlockRead (File(Pointer(Param[1].vData)^), Param[2].vData^, Param[3].W); IoError := IoResult; End; 71 : Begin BlockWrite (File(Pointer(Param[1].vData)^), Param[2].vData^, Param[3].W); IoError := IoResult; End; 72 : FileReadLine (File(Pointer(Param[1].vData)^), String(Pointer(Param[2].vData)^)); 73 : FileWriteLine (File(Pointer(Param[1].vData)^), Param[2].S); 74 : Begin TempChar := PathChar; Store (TempChar, 1); End; 75 : Begin TempBool := BitCheck(Param[1].B, Param[2].vSize, VarData[Param[2].vID]^.Data^); Store (TempBool, 1); End; 76 : BitToggle(Param[1].B, Param[2].vSize, VarData[Param[2].vID]^.Data^); 77 : BitSet(Param[1].B, Param[2].vSize, VarData[Param[2].vID]^.Data^, Param[3].O); 78 : Begin FindFirst(Param[1].S, Param[2].W, DirInfo); Move (DirInfo.Name, VarData[IdxVarDir ]^.Data^, SizeOf(DirInfo.Name)); Move (DirInfo.Size, VarData[IdxVarDir + 1]^.Data^, SizeOf(DirInfo.Size)); Move (DirInfo.Time, VarData[IdxVarDir + 2]^.Data^, SizeOf(DirInfo.Time)); Move (DirInfo.Attr, VarData[IdxVarDir + 3]^.Data^, SizeOf(DirInfo.Attr)); End; 79 : Begin FindNext(DirInfo); Move (DirInfo.Name, VarData[IdxVarDir ]^.Data^, SizeOf(DirInfo.Name)); Move (DirInfo.Size, VarData[IdxVarDir + 1]^.Data^, SizeOf(DirInfo.Size)); Move (DirInfo.Time, VarData[IdxVarDir + 2]^.Data^, SizeOf(DirInfo.Time)); Move (DirInfo.Attr, VarData[IdxVarDir + 3]^.Data^, SizeOf(DirInfo.Attr)); End; 80 : FindClose(DirInfo); 81 : Begin TempStr := JustPath(Param[1].S); Store (TempStr, 256); End; 82 : Randomize; 83 : Begin TempByte := strWordCount(ParamsStr, ' '); Store (TempByte, 1); End; 84 : Begin If Param[1].B = 0 Then TempStr := MPEName Else TempStr := strWordGet(Param[1].B, ParamsStr, ' '); Store (TempStr, 256); End; 85 : Begin TempByte := Screen.TextAttr; Store (TempByte, 1); End; 86 : Session.io.AnsiColor(Param[1].B); 87 : Begin TempStr := DirSlash(Param[1].S); Store (TempStr, 256); End; 88 : Begin TempStr := strStripPipe(Param[1].S); Store (TempStr, 256); End; 89 : Begin TempLong := Param[1].vSize; Store (TempLong, 4); End; 90 : FillChar (Param[1].vData^, Param[2].L, Param[3].C); 91 : Begin BlockWrite (File(Pointer(Param[1].vData)^), Param[2].vData^, Param[2].vSize); IoError := IoResult; End; 92 : Begin BlockRead (File(Pointer(Param[1].vData)^), VarData[Param[2].vID]^.Data^, VarData[Param[2].vID]^.DataSize); IoError := IoResult; End; 500 : Begin TempStr := Session.io.GetInput(Param[1].B, Param[2].B, Param[3].B, Param[4].S); Store (TempStr, 256); Session.io.AllowArrow := True; End; 501 : Begin TempBool := GetUserRecord(Param[1].L); Store (TempBool, 1); End; 502 : Begin TempChar := Session.io.OneKey(Param[1].S, Param[1].O); Store (TempChar, 1); End; 503 : GetUserVars(Session.User.ThisUser); 504 : Begin TempBool := Session.io.GetYN(Param[1].S, True); Store (TempBool, 1); End; 505 : Begin TempBool := Session.io.GetYN(Param[1].S, False); Store (TempBool, 1); End; 506 : Begin Session.io.OutFile(Param[1].S, True, 0); TempBool := Not Session.io.NoFile; Store (TempBool, 1); End; 507 : Begin TempBool := FileCopy(Param[1].S, Param[2].S); Store (TempBool, 1); End; 508 : Begin ReloadMenu := Session.Menu.ExecuteCommand(Param[1].S, Param[2].S); Session.io.AllowArrow := True; End; 509 : Begin Session.io.InMacroStr := Param[1].S; Session.io.InMacroPos := 1; Session.io.InMacro := Session.io.InMacroStr <> ''; End; 510 : Begin TempBool := Session.User.Access(Param[1].S); Store (TempBool, 1); End; 511 : Upgrade_User_Level(True, Session.User.ThisUser, Param[1].I); 512 : Session.SetTimeLeft(Param[1].I); 513 : Halt(0); 514 : Begin TempBool := GetMBaseRecord(Param[1].L); Store (TempBool, 1); End; 515 : Begin TempStr := Session.GetPrompt(Param[1].L); Store (TempStr, 256); End; 516 : Begin TempBool := GetMGroupRecord(Param[1].L); Store (TempBool, 1); End; 517 : Session.io.PurgeInputBuffer; 518 : Begin TempBool := GetFBaseRecord(Param[1].L); Store (TempBool, 1); End; 519 : Begin TempBool := GetFGroupRecord(Param[1].L); Store (TempBool, 1); End; 520 : Session.SystemLog(Param[1].S); 521 : Session.io.AnsiMoveX(Param[1].B); 522 : Session.io.AnsiMoveY(Param[1].B); 523 : Session.io.OutPipe(Param[1].S); 524 : Session.io.OutPipeLn(Param[1].S); 525 : Session.io.OutRaw(Param[1].S); 526 : Session.io.OutRawLn(Param[1].S); 527 : Begin TempStr := ''; If Session.io.ParseMCI(False, Param[1].S) Then TempStr := Session.io.LastMCIValue; Store (TempStr, 256); End; 528 : Begin TempInt := Session.TimeLeft; Store (TempInt, 2); End; 529 : If Param[1].B < 10 Then Begin Move (Session.io.ScreenInfo[Param[1].B].X, Param[2].vData^, 1); Move (Session.io.ScreenInfo[Param[1].B].Y, Param[3].vData^, 1); Move (Session.io.ScreenInfo[Param[1].B].A, Param[4].vData^, 1); End; 530 : If Param[1].B < FileSize(Session.PromptFile) Then Begin Seek (Session.PromptFile, Param[1].B); Write (Session.PromptFile, Param[2].S); End; 531 : Begin TempChar := Session.io.MorePrompt; Store (TempChar, 1); End; 532 : Session.io.PauseScreen; 533 : If Param[1].B <= MaxPromptInfo Then Session.io.PromptInfo[Param[1].B] := Param[2].S; 534 : Session.io.BufFlush; 535 : Begin TempStr := Session.io.StrMci(Param[1].S); Store (TempStr, 256); End; 536 : Begin TempChar := #0; If (Param[1].B < 81) and (Param[2].B < 26) Then TempChar := Screen.Buffer[Param[2].B][Param[1].B].UnicodeChar; Store (TempChar, 1); End; 537 : Begin TempByte := 0; If (Param[1].B < 81) and (Param[2].B < 26) Then TempByte := Screen.Buffer[Param[2].B][Param[1].B].Attributes; Store (TempByte, 1); End; 538 : PutUserVars(Session.User.ThisUser); 539 : PutUserRecord(Param[1].L); 540 : Begin TempBool := Session.User.FindUser(Param[1].S, False); Store (TempBool, 1); End; 541 : Begin TempBool := GetMBaseStats(Param[1].L, LongInt(Pointer(Param[2].vData)^), LongInt(Pointer(Param[3].vData)^), LongInt(Pointer(Param[4].vData)^)); Store (TempBool, 1); End; 542 : WriteXY (Param[1].B, Param[2].B, Param[3].B, Param[4].S); 543 : WriteXYPipe (Param[1].B, Param[2].B, Param[3].B, Param[4].B, Param[5].S); End; End; Procedure TInterpEngine.SkipBlock; begin NextChar; NextWord; MoveToPos (CurFilePos + W); end; Procedure TInterpEngine.DefineProcedure; Var Count : Word; VarChar : Char; Params : Word; NumVars : Word; Begin NextWord; { procedure var id } If FindVariable(W) > 0 Then Begin /// ???????????????????? Error (mpxMultiInit, ''); Exit; End; Inc (CurVarNum); New (VarData[CurVarNum]); With VarData[CurVarNum]^ Do Begin VarID := W; vType := iNone; NumParams := 0; ProcPos := 0; VarSize := 0; Datasize := 0; ArrPos := 0; Kill := False; Data := NIL; End; NextChar; Params := 0; While (ErrNum = 0) And (Not (Ch in [Char(opProcType), Char(opBlockOpen)])) Do Begin VarChar := Ch; NextWord; NumVars := W; For Count := 1 To NumVars Do Begin Inc(Params); VarData[CurVarNum]^.Params[Params] := VarChar; NextWord; VarData[CurVarNum]^.pID[Params] := W; End; NextChar; End; If Ch = Char(opProcType) Then Begin NextChar; VarData[CurVarNum]^.vType := Char2VarType(Ch); VarData[CurVarNum]^.VarSize := GetVarSize(VarData[CurVarNum]^.vType); End Else PrevChar; VarData[CurVarNum]^.NumParams := Params; VarData[CurVarNum]^.ProcPos := CurFilePos; SkipBlock; End; Procedure TInterpEngine.StatementForLoop; Var VarNum : Word; VarArray : TArrayInfo; RecInfo : TRecInfo; LoopStart : Real; LoopEnd : Real; Count : Real; CountTo : Boolean; SavedPos : LongInt; Begin NextWord; VarNum := FindVariable(W); CheckArray (VarNum, VarArray, RecInfo); LoopStart := EvaluateNumber; NextChar; CountTo := Ch = Char(opTo); LoopEnd := EvaluateNumber; Count := LoopStart; SavedPos := CurFilePos; If (CountTo And (LoopStart > LoopEnd)) Or ((Not CountTo) And (LoopStart < LoopEnd)) Then SkipBlock Else If CountTo Then While (Count <= LoopEnd) And Not Done Do Begin SetNumber(VarNum, Count, VarArray, RecInfo); MoveToPos(SavedPos); If ExecuteBlock (CurVarNum) = 1 Then Break; Count := GetNumber(VarNum, VarArray, RecInfo) + 1; End Else While (Count >= LoopEnd) And Not Done Do Begin SetNumber(VarNum, Count, VarArray, RecInfo); MoveToPos(SavedPos); If ExecuteBlock (CurVarNum) = 1 Then Break; Count := GetNumber(VarNum, VarArray, RecInfo) - 1; End; End; Procedure TInterpEngine.StatementWhileDo; Var IsTrue : Boolean; StartPos : LongInt; begin StartPos := CurFilePos; IsTrue := True; While (ErrNum = 0) And IsTrue And Not Done Do Begin IsTrue := EvaluateBoolean; If IsTrue Then Begin If ExecuteBlock (CurVarNum) = 1 Then Begin MoveToPos (StartPos); EvaluateBoolean; SkipBlock; Break; End Else MoveToPos (StartPos); End Else SkipBlock; End; End; Procedure TInterpEngine.StatementRepeatUntil; Var StartPos: LongInt; Begin StartPos := CurFilePos; Repeat MoveToPos (StartPos); If ExecuteBlock (CurVarNum) = 1 Then Begin EvaluateBoolean; Break; End; Until (ErrNum <> 0) or (EvaluateBoolean) or Done; End; Function TInterpEngine.StatementCase : Byte; Var StartPos : LongInt; EndPos : LongInt; TempStr : String; TempBol : Boolean; TempNum : Real; Found : Boolean; VarType : TIdentTypes; Numbers : Array[1..mplMaxCaseNums] of Record Num : Real; Range : Boolean; End; NumberPos : Word; Count : Word; Str : String; Begin NextWord; // statement size Result := 0; StartPos := CurFilePos; EndPos := W; Found := False; NumberPos := 0; NextChar; VarType := TIdentTypes(Byte(Ch)); Case VarType of iChar, iString : TempStr := EvaluateString; iBool : TempBol := EvaluateBoolean; Else TempNum := EvaluateNumber; End; Repeat Case VarType of iChar, iString : Repeat Str := EvaluateString; Found := Found or (TempStr = Str); NextChar; If Ch <> Char(opParamSep) Then Begin PrevChar; Break; End; Until ErrNum <> 0; iBool : Found := EvaluateBoolean = TempBol; Else Repeat Inc (NumberPos); Numbers[NumberPos].Num := EvaluateNumber; NextChar; If Ch = Char(opParamSep) Then Numbers[NumberPos].Range := False Else If Ch = Char(opNumRange) Then Numbers[NumberPos].Range := True Else Begin Numbers[NumberPos].Range := False; PrevChar; Break; End; Until ErrNum <> 0; Count := 1; Repeat If Numbers[Count].Range Then Found := (TempNum >= Numbers[Count].Num) and (TempNum <= Numbers[Count + 1].Num) Else Found := TempNum = Numbers[Count].Num; Inc (Count); Until Found or (Count > NumberPos); End; If Found Then Begin Result := ExecuteBlock (CurVarNum); MoveToPos (StartPos + EndPos); Exit; End Else SkipBlock; NextChar; If Ch = Char(opElse) Then Begin // we probably want to skip the open block here in compiler Result := ExecuteBlock(CurVarNum); Break; End Else If Ch = Char(opBlockClose) Then Break Else PrevChar; Until (ErrNum > 0) or Done; End; Function TInterpEngine.StatementIfThenElse : Byte; Var Ok : Boolean; Begin Result := 0; Ok := EvaluateBoolean; If Ok Then Result := ExecuteBlock(CurVarNum) Else SkipBlock; NextChar; If Ch = Char(opElse) Then Begin If Not Ok Then Result := ExecuteBlock(CurVarNum) Else SkipBlock; End Else PrevChar; End; Function TInterpEngine.ExecuteBlock (StartVar: Word) : Byte; Var Count : Word; BlockStart : LongInt; BlockSize : Word; Begin Result := 0; {$IFDEF LOGGING} Inc(Depth); Session.SystemLog('[' + strI2S(Depth) + '] ExecBlock BEGIN Var: ' + strI2S(StartVar) + ' Rec: ' + strI2S(StartRec)); {$ENDIF} NextChar; // block begin character... can we ignore it? at least for case_else NextWord; // or just have case else ignore the begin at the compiler level // but still output the begin BlockStart := CurFilePos; BlockSize := W; Repeat NextChar; Case TTokenOpsRec(Byte(Ch)) of {0} opBlockOpen : Begin PrevChar; Self.ExecuteBlock(CurVarNum); End; {1} opBlockClose : Break; {2} opVarDeclare : DefineVariable; {12} opSetVar : Begin NextWord; SetVariable(FindVariable(W)); End; {18} opProcDef : DefineProcedure; {19} opProcExec : ExecuteProcedure(NIL); {21} opFor : StatementForLoop; {34} opIf : Begin Result := StatementIfThenElse; If Result > 0 Then Begin MoveToPos(BlockStart + BlockSize); Break; End; End; {36} opWhile : StatementWhileDo; {39} opRepeat : StatementRepeatUntil; {47} opGoto : Begin NextWord; MoveToPos(W); End; {49} opHalt : Done := True; {50} opCase : Begin Result := StatementCase; If Result > 0 Then Begin MoveToPos(BlockStart + BlockSize); Break; End; End; {53} opBreak : Begin MoveToPos (BlockStart + BlockSize); Result := 1; Break; End; {54} opContinue : Begin MoveToPos (BlockStart + BlockSize); Result := 2; Break; End; {55} opUses : Begin Repeat NextWord; InitProcedures (Owner, Self, VarData, CurVarNum, CurVarID, W); NextChar; If Ch <> Char(opParamSep) Then Begin PrevChar; Break; End; Until ErrNum <> 0; End; {56} opExit : Begin Done := True; ExitProc := True; End; Else Error (mpxUnknownOp, strI2S(Ord(Ch))); End; Until (ErrNum <> 0) or Done or DataFile^.EOF; {$IFDEF LOGGING} Session.SystemLog('[' + strI2S(Depth) + '] ExecBlock KILL VAR: ' + strI2S(CurVarNum) + ' to ' + strI2S(StartVar + 1)); {$ENDIF} For Count := CurVarNum DownTo StartVar + 1 Do Begin {$IFDEF LOGGING} LogVarInformation(Count); {$ENDIF} If (VarData[Count]^.Kill) And (VarData[Count]^.Data <> NIL) Then Begin FreeMem(VarData[Count]^.Data, VarData[Count]^.DataSize); {$IFDEF LOGGING} Session.SystemLog(' FreeMem ' + strI2S(Count)); {$ENDIF} End; {$IFDEF LOGGING} Session.SystemLog(' Dispose ' + strI2S(Count)); {$ENDIF} Dispose (VarData[Count]); End; CurVarNum := StartVar; {$IFDEF LOGGING} Session.SystemLog('[' + strI2S(Depth) + '] ExecBlock END'); Dec (Depth); {$ENDIF} End; Function TInterpEngine.Execute (FN: String) : Byte; // 0 = not found 1 = ok 2 = goto new menu Var VerStr : String; Res : LongInt; Begin Result := 0; CurVarNum := 0; CurVarID := 0; ReloadMenu := False; Done := False; ExitProc := False; SavedMCI := Session.io.AllowMCI; SavedGroup := Session.User.IgnoreGroup; SavedArrow := Session.io.AllowArrow; DataFile := New(PCharFile, Init(mplExecuteBuffer)); Session.io.AllowArrow := True; If strWordCount(FN, ' ') > 1 Then Begin ParamsStr := Copy(FN, strWordPos(2, FN, ' '), Length(FN)); FN := strWordGet(1, FN, ' '); End Else ParamsStr := ''; If Pos('.', FN) = 0 Then FN := FN + mplExtExecute; If Pos(PathChar, FN) = 0 Then If FileExist(Session.Theme.ScriptPath + FN) Then FN := Session.Theme.ScriptPath + FN Else If Session.Theme.Flags and thmFallBack <> 0 Then FN := Config.ScriptPath + FN; MPEName := FN; If Not DataFile^.Open(FN) Then Begin Dispose(DataFile, Done); Exit; End; Result := 1; If DataFile^.FileSize < mplVerLength Then Begin DataFile^.Close; Error (mpxInvalidFile, FN); Dispose (DataFile, Done); Exit; End; DataFile^.BlockRead (VerStr[1], mplVerLength, Res); VerStr[0] := Chr(mplVerLength); If VerStr <> mplVersion Then Begin DataFile^.Close; Error (mpxVerMismatch, VerStr); Dispose (DataFile, Done); Exit; End; InitProcedures (Owner, Self, VarData, CurVarNum, CurVarID, 0); ExecuteBlock (CurVarNum); DataFile^.Close; Dispose(DataFile, Done); Session.io.AllowMCI := SavedMCI; Session.User.IgnoreGroup := SavedGroup; Session.io.AllowArrow := SavedArrow; Result := Ord(ReloadMenu) + 1; End; Function ExecuteMPL (Owner: Pointer; Str: String) : Byte; Var Script : TInterpEngine; Begin Script := TInterpEngine.Create(Owner); Result := Script.Execute(Str); If Script.ErrNum > 0 Then Session.io.OutFullLn ('|CR|12MPX ERROR: ' + Script.GetErrorMsg); Script.Free; End; End.