Aktuelle Zeit: 24.09.2017 10:30

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]




Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 16:55 
Offline
Benutzeravatar

Registriert: 01.04.2007 20:18
Hallo.

Da ich leichte Probleme hatte, die Hilfe in Bezug auf externe Werkzeuge richtig zu interpretieren,
habe ich mal schnell eine Art Grundgerüst gebastelt um einen "Preprocessor" für PB zu bauen.

Mein Plan war es ursprünglich, automatisch Includedateien einzufügen. Daher ist das Gerüst auch
mehr oder weniger darauf ausgelegt.

Mit Hilfe von Little John's LPP habe ich mir dann das ganze zusammengereimt. Damit man nicht wie ich,
am Anfang wie das berühmte Schwein vorm Uhrwerk steht und fragend blickt, weils nicht funktioniert,
ein HowTo ;)

Lange Rede kurzer Sinn :

HowTo : Wie schreibe ich einen Präprozessor für die Purebasic - IDE

Als allererstes : Was ist ein Präprozessor (engl. Preprocessor) ?

Stark vereinfacht : Der Präprozessor soll einen Code so verändern, das der Compiler mit dem Code auch etwas anfangen kann.
Damit kann man eine ganze Menge Sachen anstellen. z.B. könnte man alle im Code auftauchende Msg( in MessageRequester(
umwandeln..., einen OOP Konverter bauen (Gibt es schon), damit man in OOP Manier schreiben kann und der Compiler es auch versteht.
Die Möglichkeiten sind nur durch die Fantasie des Programmierers begrenzt.

Also schauen wir mal wie man so ein Ding zusammenbaut. Hier zeige ich ein "Grundgerüst", das man eigentlich für jeden erdenklichen
Anwendungsfall nehmen kann.

Das ganze ist stark kommentiert ;)

Code:
;:
;: Grundgerüst IDE - Tool : Präprozessor
;:
;: Einstellungen in der IDE : Menu -> Werkzeuge -> Werkzeuge konfigurieren :
;:
;: Man muss zwei externe Werkzeuge konfigurieren, damit das ganze einen Sinn macht.
;: Zum einen, wenn man in der IDE F5 drückt oder Kompilieren/Starten angeklickt wird
;:
;: Kommandozeile                        : Pfad zur exe des Präprozessors z.B. : D:\IDETools\pp.exe
;: Argumente                            : "%FILE" "%TEMPFILE" "%COMPILEFILE"
;: Arbeitsverzeichnis                   : leer lassen
;: Name                                 : Präprozessor F5 (oder wie immer es beliebt)
;: Ereignis zum Auslösen des Werkzeugs  : Vor dem kompilieren/Starten
;:
;: Auf der rechten Seite muss ausgewählt werden :
;: Warten bis zum Beenden des Werkzeugs
;:
;: Zum anderen, wenn eine Executable erstellt werden soll.
;:
;: Wenn der Präprozessor auch beim "Executable erstellen" ausgeführt werden soll
;: Ereignis zum Auslösen des Werkzeugs  : Vor dem Erstellen des Executable
;:
;: Kommandozeile                        : Pfad zur exe des Präprozessors z.B. : D:\IDETools\pp.exe
;: Argumente                            : "%FILE" "%COMPILEFILE"
;: Arbeitsverzeichnis                   : leer lassen
;: Name                                 : Präprozessor EXE (oder wie immer es beliebt)
;: Ereignis zum Auslösen des Werkzeugs  : Vor dem Erstellen des Executable
;:
;: Wenn nun F5 gedrückt wird (also der einfache Start des Sources in der IDE)
;: wird bei gespeichertem Source der Filename in den Platzhalter %FILE geschrieben.
;: daraufhin können wir mit ProgramParameter(1) diesen auslesen. (Weil auch Leerzeichen in Pfaden oder
;: Filenamen existieren dürfen, werden die Platzhalter in " gesetzt.)
;:
;: Sollte das File noch nicht gespeichert worden sein, ist der Platzhalter %FILE leer (also = "")
;: dafür ist aber im Platzhalter %TEMPFILE der Pfad und Filename zu dem Source, der von der IDE vorher
;: gespeichert wird.
;:
;: Nun haben wir den originalen Sourcecode-Filenamen mit Pfad. In dem Platzhalter %COMPILEFILE ist nun der
;: Filename, der letztendlich tatsächlich zum Compiler geschickt wird, damit dieser seine Arbeit macht.
;:
;: Also müssen wir jetzt nur noch den originalen "SourceCode" nach unserem Willen verändern, und in als
;: %COMPILEFILE abspeichern.
;:
;: Das gleiche Prinzip beim erstellen eines Executables. Nur das es da kein %TEMPFILE gibt, da die IDE nur einen
;: gespeicherten Code zum erstellen zulässt. (Daher fehlt auch ein Argument)
;:
;: Man sollte beim Einlesen des SourceCodes und beim Schreiben des Outputs, darauf achten, das das Stringformat
;: ausgelesen und wieder geschrieben wird.
;:

Global SourceFile.s ; Originaler SourceCode, der manipuliert werden soll
Global OutPutFile.s ; Der Filename des Codes, der zum Compiler geschickt wird
Global SourceFormat ; Das Stringformat des Sourcecodes.

;: Grundlagenprozedur : Start Parameter einlesen
;:                    : Dieses Grundgerüst benötigt 2 oder 3 Parameter.
;:                    : Diese müssen eingelesen werden. Am Ende brauchen wir zwei Filenamen inkl. Pfad.
;:                    : Hierbei kommen globale Variablen zum Einsatz, damit sie in allen Prozeduren genutzt
;:                    : werden können.

Procedure.i GetStartParameter()
 
  Protected ParameterIndex = 0 ; Hier der Index unserer Argumentenzeile (beginnt mit 0)
 
 
  SourceFile = ProgramParameter(ParameterIndex) ; Im ersten Parameter sollte der Filename eines gespeicherten Codes sein.
 
  ParameterIndex + 1 ; Hier setzen wir jetzt den Index ein Feld weiter
 
  If SourceFile = ""
   
    ; Wenn aber nicht, dann wurde der Code noch nicht offiziell gespeichert. Also ist es ein F5/Kompileren/Starten
    ; Ereignis, was ausgelöst wurde. Daher muss im 2. Parameter jetzt der FileName stehen.
   
    SourceFile = ProgramParameter(ParameterIndex) ; Im ersten Parameter sollte der Filename eines gespeicherten Codes sein.
    ParameterIndex + 1 ; Hier setzen wir jetzt den Index ein Feld weiter wir brauchen ja noch das OutputFile....

  EndIf
 
  ;: Anhand unserer Variable ParameterIndex, sind wir nun an der richtigen Stelle, um das Outputfile zu bekommen.
  ;: Dieser kann nun 2 sein (Weil ein Executable erstellt werden soll) oder 3 (Weil z.B. F5 gedrückt wurde)
  OutPutFile = ProgramParameter(ParameterIndex)
 
  ;: Und nun abschliessend noch eine Kontrolle ob alles vorhanden ist (Prüfung ob einer der beiden Strings leer ist)
 
  If SourceFile = "" Or OutPutFile = ""
   
    ;: Meldung an den User
    MessageRequester("Fehler", "Source/Output Datei konnte nicht gelesen werden")
    ;: Und da ohne beide ein weitermachen keinen Sinn hat.
    End
   
  EndIf
 
   
EndProcedure

;: Grundlagenprozedur : Originalen SourceCode einlesen.
;:                    : Am besten benutzt man eine Linklist um den Code einzulesen
;:                    : Hier habe ich als Rückgabewert die Anzahl der eingelesenen
;:                    : Zeilen des Sources gewählt. Das SourceFormat sollte als
;:                    : globale Variable gespeichert werden, da man es für das
;:                    : Output schreiben noch braucht
;:                    : Der FileName ist die globale Variable : SourceFile.s

Procedure.i ReadOriginalSource(List Source.s())
 
  Protected ID
 
  ID = ReadFile(#PB_Any, SourceFile) ; Das Sourcefile zum Lesen öffnen
 
  If ID ; Wenn das öffnen erfolgreich war
   
    SourceFormat = ReadStringFormat(ID) ; Das originale Stringformat ermitteln
   
    While Not Eof(ID) ; Solange das Ende des Files nicht erreicht wurde, das ganze nochmal
     
      AddElement(Source()) ; Der Liste Source ein Element hinzufügen
      Source() = ReadString(ID, SourceFormat) ; Jetzt eine Zeile im richtigen Format einlesen
     
    Wend
   
    CloseFile(ID) ; Und das File wieder schliessen
   
  Else ; wenn das öffnen nicht geklappt hat
   
    ;: Meldung an den Nutzer
    MessageRequester("Fehler", "Source konnte nicht gelesen werden!")
    ;: Und ohne Source, macht es keinen Sinn weiterzumachen, also Programmende
    End
   
  EndIf
 
  ProcedureReturn ListSize(Source())
 
EndProcedure

;: Grundlagenprozedur : OutPutCode schreiben.
;:                    : Auch hierfür habe ich eine Liste gewählt um die einzelnen Codezeilen
;:                    : zu speichern. Desweiteren braucht man dann hier das Stringformat aus dem originalen
;:                    : Sourcecode, damit das ganze auch von dem Compiler ordnungsgemäß eingelesen und
;:                    : verarbeitet werden kann.
;:                    : Der FileName ist die globale Variable : OutputFile.s

Procedure.i WriteOutputSource(List OutPut.s())
 
  Protected ID
 
  If ListSize(OutPut()) > 0 ; Kurze Kontrolle, ob überhaupt in der Liste etwas enthalten ist.
   
    ID = CreateFile(#PB_Any, OutPutFile) ; File zum schreiben erstellen (oder überschreiben)
   
    If ID ; Wenn das File also erstellt wurde
     
      WriteStringFormat(ID, SourceFormat) ; Das Stringformat des originalen Sourcecodes schreiben
     
      ForEach Output() ; Jetzt jedes Element der Liste aufrufen
       
        WriteStringN(ID, OutPut(), SourceFormat) ; Und im originalen Stringformat in die Datei schreiben
       
      Next
     
      CloseFile(ID) ; Und das File schliessen
     
    Else ; Und wenn das File nicht erstellt wurde....
     
      ;: Meldung an den Nutzer
      MessageRequester("Fehler", "Output konnte nicht geschrieben werden!")
      ;: In den meisten Fällen wird das Programm hiernach nichts weiteres mehr machen
      ;: aber ich verzichte hier auf das END, für den Fall, dass man z.B. irgendwas löschen
      ;: möchte (TempFiles) ....
     
    EndIf
   
  EndIf
 
EndProcedure


;: Damit hätten wir schonmal die wichtigsten Prozeduren abgehakt.
;: Was man dann mit den Listen Source() und Output() anstellt, bleibt jedem selbst überlassen ;)

;: Hier jetzt noch das Programm selbst...

;: Definieren unserer beiden Listen
Define NewList Source.s() ; Die Liste des Originalen Sourcecodes
Define NewList OutPut.s() ; Und die Liste des Outputs definieren

GetStartParameter() ; Die Startparameter ermitteln
ReadOriginalSource(Source())    ; Den originalen Source einlesen

;: Nun möchte ich, das in jedem meiner Codes z.B. eine Konstante und eine Globale Variable deklariert wird
;: Das geschieht immer zeilenweise, so als würde man einen Code schreiben ;)

;: Element erstellen : Element mit Wert füllen
AddElement(Output()) : Output() = "#MeineKonstante = 123"
AddElement(Output()) : Output() = "" ; Eine Leere Zeile. Muss man nicht...
AddElement(Output()) : Output() = ~"Global MeinText.s = \"PräprozessorGrundgerüst\""
AddElement(Output()) : Output() = "" ; Eine Leere Zeile. Muss man nicht.

;: Man könnte auch gleich ganze IncludeFiles hier einbinden.
;: Diese werden genauso gelesen wie der Originale Source (nur mit einer anderen LinkList,
;: oder eingelesenen Zeilen einfach direkt an die Source() Liste anhängen)

;: An dieser Stelle hänge ich hier die Liste Source() an die Liste Output() dran

ForEach Source()
  AddElement(Output())
  Output() = Source()
Next

;: Nun schreiben wir unsere Manipulation zurück und dieses wird am Ende kompiliert
WriteOutputSource(OutPut())

;: Fertig


So wenn man dann alles fertig hat, sprich das Gerüst zu einem ausführbarem Programm gemacht hat, die IDE mit den
externen Werkzeugeinstellungen beglückt hat, kann man eigentlich schon loslegen und z.B. folgenden Code schreiben
(Es dient die Manipulation des obigen Grundgerüst als Vorlage)

Code:
Debug #MeineKonstante
Debug MeinText


Nun werden bei Druck auf F5 in der Debug-Ausgabe
Code:
123
PräprozessorGrundgerüst

erscheinen.

Der Compiler bekommt jetzt nicht mehr nur den Code
Code:
Debug #MeineKonstante
Debug MeinText

sondern der Präprozessor macht daraus
Code:
#MeineKonstante = 123

Global MeinText.s = "PräprozessorGrundgerüst"

Debug #MeineKonstante
Debug MeinText


Es ersetzt mitnichten eine Userlibrary. Es funktioniert z.B. keine Autovervollständigung. Oder eine Anzeige in der Statusleiste der IDE.
Aber selbst das kann man ändern.... nur mehr Aufwand, und vermutlich dann nicht mehr Crossplatform (Windows/MacOS/Linux)

Ich hoffe ich habe das ganze verständlich gemacht, und das Forum wird von Präprozessorcodes nur so überschwemmt :mrgreen:

_________________
PureBasic 5.45 LTS / 5.61 (Windows x86/x64) | Windows10 Pro x64 | Z87-PLUS | i7 4770k | 32GB RAM | iChill GeForce GTX 980 X4 Ultra | HAF XF Evo​​


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 18:15 
Offline
Benutzeravatar

Registriert: 04.08.2009 17:24
Leider mit den üblichen Nachteilen:

- Fehlermeldungen werden nicht auf die richtige Zeile zurückgeführt
- Debuggen nicht möglich

Für das Startfile kann man das noch hinbiegen, aber spätestens wenn Includefiles im Spiel sind, ist es endgültig vorbei.


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 18:32 
Offline
Benutzeravatar

Registriert: 24.11.2004 13:12
Wohnort: Germany
Das habe ich früher bei meinen OOP-Precompiler so gelöst das nur Änderungen in einer Zeile durchgeführt werden und neue Include-Dateien erstellte und eingebunden wurde.

P.S. In der ersten Zeile ein 'IncludeFile "TopFile.xxx" : 'und in der letzen Zeile ein 'IncludeFile "BottomFile.xxx"' eingefügt.
Somit verschiebt sich nichts.
Code:
 ; Include TopFile
  FirstElement(Lines())
  Lines() = "Includefile " + #DQUOTE$ + topfile + #DQUOTE$ + " : " + Lines()
 
  ; letzte Zeile suchen
  LastElement(Lines())
  While Left(Lines(), 1) = ";"
    PreviousElement(Lines())
  Wend
 
  ; Include BottomFile
  AddElement(Lines())
  Lines() = "Includefile " + #DQUOTE$ + bottomfile + #DQUOTE$


In den automatisch erstellen Top und Bottom Files weitere includes, Macros, Proceduren, etc angelegt

_________________
Alles ist möglich, fragt sich nur wie...
Projekte EventDesigner v1.x / OOP-BaseClass-Modul / OPC-Helper DLL
PB v3.30 / v5.4x - OS Mac Mini OSX 10.xx / Window 10 Pro. (X64) /Window 7 Pro. (X64) / Window XP Pro. (X86) / Ubuntu 14.04
Downloads auf Webspace


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 18:56 
Offline
Benutzeravatar

Registriert: 01.04.2007 20:18
Josh hat geschrieben:
Leider mit den üblichen Nachteilen:

- Fehlermeldungen werden nicht auf die richtige Zeile zurückgeführt
- Debuggen nicht möglich

Für das Startfile kann man das noch hinbiegen, aber spätestens wenn Includefiles im Spiel sind, ist es endgültig vorbei.


Deswegen schieb ich auch :
Zitat:
Es ersetzt mitnichten eine Userlibrary

Aber mit der Technik, wie mk-soft es beschreibt (alles in eine Zeile), kann es zumindest nur um eine Zeile verrutscht noch hinhauen ohne das es kompliziert wird. Das Beispiel mit Automatisch Includefiles einbinden, war halt für mich selbst...

Aber im Grunde ist es ja jedem überlassen was damit gemacht wird, es geht ja nur um das WIE.... nicht um das WAS ;)

_________________
PureBasic 5.45 LTS / 5.61 (Windows x86/x64) | Windows10 Pro x64 | Z87-PLUS | i7 4770k | 32GB RAM | iChill GeForce GTX 980 X4 Ultra | HAF XF Evo​​


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 19:04 
Offline
Benutzeravatar

Registriert: 24.11.2004 13:12
Wohnort: Germany
Zitat:
Aber mit der Technik, wie mk-soft es beschreibt (alles in eine Zeile), kann es zumindest nur um eine Zeile verrutscht noch hinhauen ohne das es kompliziert wird. Das Beispiel mit Automatisch Includefiles einbinden, war halt für mich selbst...

Bei mir verrutscht keine Zeile :wink:

Der PreCompiler war echt klein. Keine 1000 Zeilen.
Wenn jemand daran Interesse hat, kann ich ja mal den PreCompiler mal Veröffentlichen. 8)

_________________
Alles ist möglich, fragt sich nur wie...
Projekte EventDesigner v1.x / OOP-BaseClass-Modul / OPC-Helper DLL
PB v3.30 / v5.4x - OS Mac Mini OSX 10.xx / Window 10 Pro. (X64) /Window 7 Pro. (X64) / Window XP Pro. (X86) / Ubuntu 14.04
Downloads auf Webspace


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 19:07 
Offline
Benutzeravatar

Registriert: 01.04.2007 20:18
Interesse :allright:

Auch ein alter "Gaul" wie ich, kann manchmal noch was lernen :mrgreen:

_________________
PureBasic 5.45 LTS / 5.61 (Windows x86/x64) | Windows10 Pro x64 | Z87-PLUS | i7 4770k | 32GB RAM | iChill GeForce GTX 980 X4 Ultra | HAF XF Evo​​


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: [Anfänger][HowTo] : Präprozessor als IDE Tool
BeitragVerfasst: 13.09.2017 19:27 
Offline
Benutzeravatar

Registriert: 24.11.2004 13:12
Wohnort: Germany
Schon Online :allright:

Link: viewtopic.php?f=9&t=30367

_________________
Alles ist möglich, fragt sich nur wie...
Projekte EventDesigner v1.x / OOP-BaseClass-Modul / OPC-Helper DLL
PB v3.30 / v5.4x - OS Mac Mini OSX 10.xx / Window 10 Pro. (X64) /Window 7 Pro. (X64) / Window XP Pro. (X86) / Ubuntu 14.04
Downloads auf Webspace


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast


Sie dürfen keine neuen Themen in diesem Forum erstellen.
Sie dürfen keine Antworten zu Themen in diesem Forum erstellen.
Sie dürfen Ihre Beiträge in diesem Forum nicht ändern.
Sie dürfen Ihre Beiträge in diesem Forum nicht löschen.

Suche nach:
Gehe zu:  

 


Powered by phpBB © 2008 phpBB Group | Deutsche Übersetzung durch phpBB.de
subSilver+ theme by Canver Software, sponsor Sanal Modifiye