Fragen zur Programmierung eines Interpreters

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Fragen zur Programmierung eines Interpreters

Beitrag von Kurzer »

Hallo Kollegen,

ich möchte mein Programm mit einer Art kleinen Skriptsprache versehen, so dass der Anwender eigene Skripte erstellen kann, die mein Programm dann verarbeitet bzw. ausführt. Es geht dabei um die Bearbeitung von Texten.

Bei bestimmten Teilen der Umsetzung ist mir noch nicht klar wie ich es am besten realisieren sollte, daher richtet sich meine Frage an Leute die so etwas schon mal programmiert haben.

Aber erst nochmal kurz erklärt was das eigentlich werden soll. Im Prinzip soll diese Skriptsprache wie eine Macrosprache mit den Textdaten in einem Texteditor arbeiten können. Das vom User erstellte Script soll in der Lage sein ein Ergebnis-Array mit Daten zu füllen die aus einer Textdatei extrahiert werden. Ein Script kann dabei immer nur mit einer Textdatei arbeiten.
Denkbare Befehle wären z.B.:

Code: Alles auswählen

GotoLine 30 ; Setzt den Lesepointer in der Textdatei auf den Anfang der 30.ten Zeile
GotoOffset 185 ; Setzt den Lesepointer in der Textdatei vor das 185.te Zeichen
GetString(5) ; Gibt ausgehened vom aktuellen Lesepointer die nächsten 5 Zeichen als String zurück
Die komplexeren Dinge wie Variablen und Bedingungen (If, Else, Endif) lasse ich erstmal außen vor.

Ich würde die Behandlung und Ausführung der Sprache in zwei Schritten durchführen.

1) Tokenizen, also die Befehle und deren Parameter und ggf. Operatoren in numerische Tokens wandeln, die ich dann als 2-dimensionales Array im Speicher halte.
2) Das 2D-Array stellt dann quasi das Script-Programm dar und wird von einer "virtuellen CPU" sequentiell abgearbeitet.

Das Array könnte so aussehen:

Code: Alles auswählen

Token, StringParameter1, StringParameter2, IntParamerter1, IntParamerter2, Operator
Ich würde in diesem Array Felder für alle möglichen Parametertypen haben, es würden dann aber nur jene ausgefüllt werden, die der entsprechende Token benötigt. Bei dem Token für GotoLine würde also nur IntParamerter1 genutzt werden, weil "GotoLine" nur einen numerischen Parameter hat.

Das ganze geht sicherlich eleganter, mir ist bisher nur noch nichts anderes dafür eingefallen.

Was mir auch noch nicht ganz klar ist:
Wie gestalte ich den Aufruf der zu einem Token gehörenden Prozedur? Ich könnte ein weiteres Array nutzen in dem ich zu jedem Token die Prozedure-Adresse eingetrage (oder der Tokenizer schreibt die entsprechende Prozedur-Adresse bereits in das Token-Array, dann spart man sich das weitere Adressen-Array):

Code: Alles auswählen

Token, Prozedure-Adresse
1, @GotoLine() ; Adresse von Prozedure GotoLine() / 1 = GotoLine
2, @GotoOffset() ; Adresse von Prozedure GotoOffset() / 2 = GotoOffset
3, @GetString() ; Adresse von Prozedure GetString .... usw.
So könnte man dann mit einem Prototype arbeiten und die Prozedur ungefähr so aufrufen:

Code: Alles auswählen

Prototype.i Command()
Cmd.Command = [Adresse] ; Hier die Prozedur-Adresse aus der o.g. Tokenliste eintragen.
Cmd() ; Und die Prozedur aufrufen
Hier fragt sich wie man es am besten mit den Parametern und den Operatoren macht. Die Parameter kann ich beim Prototype nicht mit angeben, weil sie unter Umständen für jeden Skriptbefehl anders sind. Ich könnte ein globales "Parameter-Übergabearray" bzw. eine Struktur nutzen, welches wieder jeweils ein Feld für alle möglichen Parameter/Operatoren aufnimmt. Die "virtuelle CPU", die den tokenisierten Code abarbeitet, muss dann die entsprechenden Werte des Tokens dort hinein übertragen und die danach aufgerufene Prozedur müsste sich die für sie passenden Werte von dort herausnehmen.

Globales Parameter-Array: Hierrauf greift jede der aufgerufenen "Befehls-Prozeduren" zu.

Code: Alles auswählen

StringParameter1, StringParameter2, IntParamerter1, IntParamerter2, Operator
Das würde vermutlich alles irgendwie gehen, aber sicherlich funktioniert es auch eleganter bzw. einfacher, so dass man nicht immer die Felder für alle möglich vorkommenden Parameter in den Arrays/Strukturen bereithalten muss.

Leider ist dies mein allererster Interpreter, den ich schreibe. Habt ihr dazu ein paar Tipps für mich?
Vielen Dank

Kurzer
Zuletzt geändert von Kurzer am 27.04.2019 12:56, insgesamt 1-mal geändert.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Fragen zur Programmierung eines Interpreters

Beitrag von Sicro »

Hallo Kurzer,

wenn du nichts gegen die Verwendung meines Lexer-Moduls hast, kann ich dir heute Abend oder morgen ein Grundgerüst basteln.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Fragen zur Programmierung eines Interpreters

Beitrag von mk-soft »

Wenns nur für Window ist, verwende doch einen fertigen Interpreter...

Link: viewtopic.php?f=8&t=31072
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Fragen zur Programmierung eines Interpreters

Beitrag von Kurzer »

Vielen Dank euch beiden für die Angebote. :allright:
Ich würde es jedoch gern selbst umsetzen wollen und auch extrem einfach halten. Habe mir zwar mit meiner Erkältung gerade einen ungünstigen Zeitpunkt ausgesucht, aber mal gucken was so geht.

Eure codes schaue ich mir auf jeden Fall an, um zu sehen, ob ich daraus lernen kann.

Mittlerweile bin ich auch auf dieses Tutorial von puretom gestoßen und lese das gerade: viewtopic.php?f=9&t=27329
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Fragen zur Programmierung eines Interpreters

Beitrag von juergenkulow »

Hallo Kurzer,

Code: Alles auswählen

Procedure   PGotoLine(Zeile.i) : MessageRequester("GotoLine",Str(Zeile)) : EndProcedure
Procedure   PGotoOffset(Zeichen.i) : MessageRequester("GotoOffset",Str(Zeichen)) : EndProcedure
Procedure.s PGetString(Zeichen.i) : MessageRequester("GetString",Str(Zeichen)) : ProcedureReturn("ABCDE") : EndProcedure

Read.s Zeile.s
While Zeile<>"End"
  Kommentar=FindString(Zeile,";")
  If Kommentar 
    Zeile=Left(Zeile,Kommentar-1)
  EndIf 
  ; Debug Zeile+"#"
  Zeile=ReplaceString(Zeile,"("," ")
  Zeile=ReplaceString(Zeile,")"," ")
  Leerzeichen=FindString(Zeile," ")
  If Leerzeichen
    Befehl.s=Left(Zeile,Leerzeichen-1)
    Parameter=Val(Mid(Zeile,Leerzeichen))
    ; Debug Befehl
    ; Debug Str(Parameter)
  Else
    Befehl=Zeile
    Parameter=0
  EndIf 
  Select Befehl
    Case "GotoLine": PGotoLine(Parameter) 
    Case "GotoOffset": PGotoOffset(Parameter)
    Case "GetString" : Debug PGetString(Parameter)
    Default : Debug "unbekannter Befehl"
  EndSelect ; Könnte Befehle auch in Tokenliste eintragen und später in verschieden Formaten ausführen.
  Read.s Zeile
Wend

DataSection
  KurzerCode:
  Data.s "GotoLine 30 ; Setzt den Lesepointer in der Textdatei auf den Anfang der 30.ten Zeile"
  Data.s "GotoOffset 185 ; Setzt den Lesepointer in der Textdatei vor das 185.te Zeichen"
  Data.s "GetString(5) ; Gibt ausgehened vom aktuellen Lesepointer die nächsten 5 Zeichen als String zurück"
  Data.s "End" ; Brauche eine Abbruchbedingung, sonst [ERROR] Fehler beim Einlesen von Daten: keine weiteren Daten.
EndDataSection
Gruß
Bitte stelle Deine Fragen, denn den Erkenntnisapparat einschalten entscheidet über das einzig bekannte Leben im Universum.

Jürgen Kulow Wersten :D_üsseldorf NRW D Europa Erde Sonnensystem Lokale_Flocke Lokale_Blase Orion-Arm
Milchstraße Lokale_Gruppe Virgo-Superhaufen Laniakea Sichtbares_Universum
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Fragen zur Programmierung eines Interpreters

Beitrag von Kurzer »

Hallo Jürgen,

danke für deinen Beitrag. :allright: 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
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Fragen zur Programmierung eines Interpreters

Beitrag von juergenkulow »

Hallo Kurzer,

schlage vor die Entwicklung zu starten mit: MOV rcx,qword ..

Code: Alles auswählen

c:pbcompiler interpre.pb /Commented

Code: Alles auswählen

; Select Befehl
  PUSH   qword [v_Befehl]
; Case "GotoLine": PGotoLine(Parameter)
  MOV    rdx,_S1
  MOV    rcx,[rsp]
  SUB    rsp,40
  CALL   SYS_StringEqual
  ADD    rsp,40
  OR     rax,rax
  JE    _Case1
  SUB    rsp,8
  MOV    rcx,qword [v_Parameter]
  SUB    rsp,32
  CALL  _Procedure0
  ADD    rsp,40
; Case "GotoOffset": PGotoOffset(Parameter)
  JMP   _EndSelect1
_Case1:
  MOV    rdx,_S2
  MOV    rcx,[rsp]
  SUB    rsp,40
  CALL   SYS_StringEqual
  ADD    rsp,40
  OR     rax,rax
  JE    _Case2
  SUB    rsp,8
  MOV    rcx,qword [v_Parameter]
  SUB    rsp,32
  CALL  _Procedure2
  ADD    rsp,40
; Case "GetString" : s.s=PGetString(Parameter) ; Änderung zum Ursprungsprogramm, da in EXE der Debug-Befehl nicht aufgerufen wird.
  JMP   _EndSelect1
_Case2:
  MOV    rdx,_S3
  MOV    rcx,[rsp]
  SUB    rsp,40
  CALL   SYS_StringEqual
  ADD    rsp,40
  OR     rax,rax
  JE    _Case3
  MOV    rax,[PB_StringBasePosition]
  PUSH   rax
  SUB    rsp,8
  PUSH   rax
  MOV    rcx,qword [v_Parameter]
  SUB    rsp,32
  CALL  _Procedure4
  ADD    rsp,48
  LEA    rcx,[v_s]
  POP    rdx
  SUB    rsp,40
  CALL   SYS_AllocateString4
  ADD    rsp,40
; Default : Debug "unbekannter Befehl"
  JMP   _EndSelect1
_Case3:
; EndSelect 
_Case4:
_EndSelect1:
Gruß
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Fragen zur Programmierung eines Interpreters

Beitrag von Kurzer »

juergenkulow hat geschrieben:schlage vor die Entwicklung zu starten mit: MOV rcx,qword ..
Ähm, warum? :shock: Wie soll mir Assembler hierbei helfen?

Davon ab, komme ich ganz gut voran. Der Tokenizer ist fast fertig, es fehlt noch eine Preprocessing Routine, die mir verschachtelte Kommandos serialisiert. Also sowas hier:

Code: Alles auswählen

GotoChar(GetNumber(...))
Wo also eine Funktion wiederum der Parameter eines Befehls/Funktion ist. Das muss in zwei separate Kommandos gesplittet werden bevor es in Tokens umgewandelt wird.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Antworten