Hallo Jürgen,
danke für deinen Beitrag.
So in dem Bereich sollte sich das bewegen.
Allerdings trenne ich trotzdem die Aufgabe in mehrere Teile.
1) Interpretation und Prüfung des Quellcodes
2) Umwandlung der Befehle in numerische Tokens.
3) Abarbeiten des auf Tokens basierten Programms.
Ich fange die Entwicklung allerdings von hinten an, also zuerst den Teil, der das in Tokens umgewandelte Programm ablaufen lässt. Ich beschränke mich in der Entwicklungsphase erstmal auf ein paar Befehle.
Ein "tokenisierter" Programmcode liegt bei mir in einem Array vor und je ein Commandoeintrag hat folgenden Aufbau (es ist wie gesagt alles noch absoluter Testcode, Änderungen sind jederzeit möglich):
Code: Alles auswählen
Structure ProgramRecord
iToken.i
sParam1.s
sParam2.s
sParam3.s
sParam4.s
sParam5.s
sParam6.s
EndStructure
Die aktuelle Programmzeile, in der sich das Commando/Token befindet, ergibt sich aus dem Index des Arrays. Ich habe mich dazu entschlossen jeden Parameter erstmal als String zu speichern. Eine Prüfung auf Datentyp muss dann im Schritt davor beim Umwandeln von Quellcode in Tokens stattfinden. Der Grund hierfür ist, dass ich die Strukturen in der Entwicklungsphase erstmal relativ "flach" halten kann, um nicht den Überblick zu verlieren. Später kann man sicherlich auch Adress- oder Indexpointer als Params nutzen, die dann auf den jeweiligen Parameter zeigen, der dann im korrekten Datentyp vorliegt.
Jedes eindeutige Token (Commando) hat bei mir noch folgende Information:
Code: Alles auswählen
Structure TokenRecord
sName.s
iProcedureAddress.i
iLastResultType.i
iFunctionResultType.i
EndStructure
Zum einen der Klartextname. Dies wird später der Programmteil nutzen, der den Quellcodes in Tokens umwandeln muss.
Dann die Adresse der Prozedur die die Funktionalität des betreffenden Befehls/Tokens abbildet und dann noch zwei Bedingungen die für die Klassifizierung des Tokens wichtig sind: Typ des Ergebnisses des Tokens davor und der Typ des Ergebnisses des aktuellen Tokens.
Diese beiden Daten sind wichtig, damit der Programmteil, der den Quellcode in Tokens umwandelt anhand der Datentypen das korrekte Token auswählt.
So kann zum Beispiel der Operator "+" für die Addition von zwei Integerzahlen zuständig sein. Er kann aber auch zwei Strings miteinander verketten. Im Quellcode ist das immer ein + Zeichen, aber im "tokenisierten" Programm müssen das zwei separate Tokens sein, die jeweils ihre eigene Prozedur besitzen. Die eine addiert Zahlen, die andere verkettet Strings.
Weiterhin gibt es eine globale "Register-Struktur", die von allen Programmteilen gelesen und beschreiben werden kann:
Code: Alles auswählen
Structure Registers
iProgramPointer.i
iQuit.i
iLastResultType.i
sLastResult.s
iLastResult.i
EndStructure
Hier ist der aktuelle Programmzeiger abgespeichert. Also quasi die Zeilennummer innerhalb des "tokenisierten" Programms. Dieser würde z.B. bei einem "GotoProgrammzeile" Kommando wichtig sein und entsprechend verändert werden. Dann sind dort der Datentyp und das Ergebnis des letzten Kommandos/Tokens gespeichert, sowie ein "Quit-Flag", welches vom Befehl "End" gesetzt wird und die Bearbeitungsschleife für die Programmausführung beendet.
Mein Testcode befindet sich mitten in der Entwicklung und einige Teile sind nicht ausprogrammiert, aber zumindets ist er compilierbar und gibt ein paar Daten aus. Es arbeitet jedoch noch nicht mit einem Daten(text)block, obwohl ich bereits einen definiert habe.
Das eigentliche "Tokenprogramm" beginnt in Zeile 147 und besteht aus folgenden Befehlen:
Code: Alles auswählen
GotoChar 33 ; Gibt die aktuelle (wirklich angesprungene) Position im Datenfile zurück. In dem Fall Position Nummer 33
+ 12 ; Addiert 12 auf den Wert des letzten Befehls. Also 33 + 12 und gibt demnach 45 zurück
GetString(5) ; Die Funktion gibt derzeit nur den String "Vier" zu rück.
+ " Meter" ; Verkettet den zuletzt zurückgegeben String "Vier" mit dem String " Meter" und gibt demnach "Vier Meter" zurück.
Hier mal mein bisheriger Testcode:
Code: Alles auswählen
EnableExplicit
Enumeration ResultTypes
#ResultType_ANY
#ResultType_STRING
#ResultType_INTEGER
EndEnumeration
Structure TokenRecord
sName.s
iProcedureAddress.i
iLastResultType.i
iFunctionResultType.i
EndStructure
Structure ProgramRecord
iToken.i
sParam1.s
sParam2.s
sParam3.s
sParam4.s
sParam5.s
sParam6.s
EndStructure
Structure Registers
iProgramPointer.i
iQuit.i
iLastResultType.i
sLastResult.s
iLastResult.i
EndStructure
Structure Datafile
iReadPointer.i
iSizeInChars.i
iLastLine.i
sData.s
EndStructure
Prototype.i RunCmdProto()
Global stRegisters.Registers
Global Dim stTokens.TokenRecord(99)
Global Dim stProgram.ProgramRecord(9)
Global stDatafile.Datafile
Global.s sCode
sCode = "GotoChar 5 : GetStringPart(0, 10) : End"
stDatafile\iLastLine = 17
stDatafile\iReadPointer = 1
stDatafile\sData ="Zeile 1: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 2: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 3: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 4: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 5: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 6: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 7: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 8: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks und" + #CR$ + #LF$ +
"Zeile 9: Hier befindet sich Position Nummer: 821" + #CR$ + #LF$ +
"Zeile 10: Der Interpreter beschäftigt sich nicht mit dem Parsen des ursprünglichen Ausdrucks."
stDatafile\iSizeInChars = Len(stDatafile\sData)
Procedure InitTokenArray()
Protected.s sTokenName
Protected.i i=0
Restore Tokendef
Read.s sTokenName
While sTokenName <> "*"
stTokens(i)\sName = sTokenName
Read.i stTokens(i)\iProcedureAddress
Read.i stTokens(i)\iLastResultType
Read.i stTokens(i)\iFunctionResultType
i + 1
Read.s sTokenName
Wend
ReDim stTokens(i-1)
EndProcedure
Procedure ShowRegisters()
Debug "PC: " + Str(stRegisters\iProgramPointer)
If stRegisters\iLastResultType = #ResultType_STRING
Debug "LastResult: " + stRegisters\sLastResult
ElseIf stRegisters\iLastResultType = #ResultType_INTEGER
Debug "LastResult: " + Str(stRegisters\iLastResult)
EndIf
Debug "Data ReadPointer: " + Str(stDatafile\iReadPointer)
EndProcedure
Procedure Cmd_GetStringPart()
Debug "GetString (" + stProgram(stRegisters\iProgramPointer)\sParam1 + ")"
stRegisters\iLastResultType = #ResultType_STRING
stRegisters\sLastResult = "Vier"
EndProcedure
Procedure Cmd_GotoChar()
Protected.i iNewReadPointer
iNewReadPointer = Val(stProgram(stRegisters\iProgramPointer)\sParam1)
If iNewReadPointer <= stDatafile\iSizeInChars
stDatafile\iReadPointer = iNewReadPointer
Else
stDatafile\iReadPointer = stDatafile\iSizeInChars
EndIf
stRegisters\iLastResultType = #ResultType_INTEGER
stRegisters\iLastResult = iNewReadPointer
ShowRegisters()
EndProcedure
Procedure Cmd_PlusInt()
Debug Str(stRegisters\iLastResult) + " + " + stProgram(stRegisters\iProgramPointer)\sParam1
stRegisters\iLastResultType = #ResultType_INTEGER
stRegisters\iLastResult = stRegisters\iLastResult + Val(stProgram(stRegisters\iProgramPointer)\sParam1)
EndProcedure
Procedure Cmd_PlusStr()
Debug stRegisters\sLastResult + " + " + stProgram(stRegisters\iProgramPointer)\sParam1
stRegisters\iLastResultType = #ResultType_STRING
stRegisters\sLastResult = stRegisters\sLastResult + stProgram(stRegisters\iProgramPointer)\sParam1
EndProcedure
Procedure Cmd_End()
stRegisters\iQuit = #True
EndProcedure
Procedure ProcessProgramArray(Array stProgram.ProgramRecord(1))
Protected prRunCmd.RunCmdProto
prRunCmd = stTokens(stProgram(stRegisters\iProgramPointer)\iToken)\iProcedureAddress
prRunCmd()
Select stRegisters\iLastResultType
Case #ResultType_INTEGER
Debug stRegisters\iLastResult
Default
Debug stRegisters\sLastResult
EndSelect
stRegisters\iProgramPointer + 1
If stRegisters\iProgramPointer = ArraySize(stProgram()) + 1
Cmd_End()
EndIf
EndProcedure
; --------------------------------------------
InitTokenArray()
; GotoChar 33
stProgram(0)\iToken = 2
stProgram(0)\sParam1 = "33"
; PlusInt
stProgram(1)\iToken = 3
stProgram(1)\sParam1 = "12"
; GetString(5)
stProgram(2)\iToken = 1
stProgram(2)\sParam1 = "5"
; PlusStr
stProgram(3)\iToken = 4
stProgram(3)\sParam1 = " Meter"
; End
stProgram(4)\iToken = 0
Repeat
ProcessProgramArray(stProgram())
Until stRegisters\iQuit = #True
Debug "Quit it"
; ShowVariableViewer()
; CallDebugger
; --------------------------------------------
DataSection
Tokendef:
; Commandname, Address of procedure, Type of last result, Type of command/function result
; TokenNr = 0
Data.s "end"
Data.i @Cmd_End(), #ResultType_ANY, #ResultType_ANY
; TokenNr = 1
Data.s "getstringpart"
Data.i @Cmd_GetStringPart(), #ResultType_ANY, #ResultType_STRING ; Extracted String
; TokenNr = 2
Data.s "gotochar"
Data.i @Cmd_GotoChar(), #ResultType_ANY, #ResultType_INTEGER ; New position of the read pointer
; TokenNr = 3
Data.s "+"
Data.i @Cmd_PlusInt(), #ResultType_INTEGER, #ResultType_INTEGER ; Result of the numerical addition
; TokenNr = 4
Data.s "+"
Data.i @Cmd_PlusStr(), #ResultType_STRING, #ResultType_STRING ; Result of string concatenation
Data.s "*" ; Ende-Markierung der Datenliste
EndDataSection
Gruß Kurzer