Hi all,
according to another discussion, I've revived an old tool of mine that allows for PureBasic statements on multiple lines. It was mainly developed as tool for the PureBasic IDE, but can also be used as standalone program.
//edit:
I've rewritten the whole program, so that it now can handle include files. I was able to do so because srod gave me some fine code, and his kind permission to abuse it.
Bug reports, suggestions for improvement and other comments are welcome.
Version 0.10
- First public release of LPP.
 
- Pre-processed include files are not merged into a single file anymore.
So in case of a syntax error, the IDE now shows the correct line. - Fixed some bugs.
 
- Additional argument when used as IDE tool, so that LPP now can handle code which has not yet been saved to a file.
 - Reading the environment variables PB_TOOLS_IDE and PUREBASIC_HOME makes LPP now more flexible and versatile.
This e.g. makes it easier to use LPP on systems where both the x86 version and the x64 version of PB is installed. - LPP now takes into account, that on Windows not only the back slash but also the forward slash can be used as path separator.
 - Better adaptation to Mac OS.
 - Changed some details.
 
- Added support for #PB_Compiler_FilePath in Include statements.
This constant is very useful for nested include files. - Improved error handling.
 - Improved comments.
 - Changed several details.
 
- LPP including documentation
- LppClean (companion program for deleting temporary files)
- Demo 1
- Demo 2 (consists of two files)
Regards, Little John
LPP including documentation
Code: Select all
; -- Little Purebasic Preprocessor (LPP)
; -- Version 0.61, 2011-01-01
; Public Domain, written by Juergen Luethje <http://luethje.eu/>.
; Developed with Purebasic 4.51.
; Cross-platform, Unicode compliant.
; Tested on Windows XP x86, and Ubuntu 10.10 x86.
; Standard disclaimer: USE AT YOUR OWN RISK!
;
; Features
; --------
; Optional line continuation in the source code by adding a particular
; mark at the end of the regarding lines. This is possible in the main
; file and in include files as well.
;
; Installation and call
; ---------------------
; Compile the program to an executable file.
;
; 1) This program was mainly developed as tool for the Purebasic IDE.
;    In the IDE, choose from the menu "Configure Tools ...".
;    In the window that appears, add the program 2 times with
;    different names by clicking the [ New ] button.
;
;    Use as trigger
;    - for one name      : Before Compile/Run
;    - for the other name: Before Create Executable
;
;    and for both names choose the following settings
;
;    Arguments: "%FILE" "%COMPILEFILE" "%TEMPFILE"    (with the quotes!)
;    [v] Wait until tool quits
;    [v] Hide Tool from the Main menu
;
; 2) You can use it as standalone program, for instance in order to
;    pre-process code that is going to be tested with PureUnit. In this
;    case, it might be useful to set the environment variable
;    PUREBASIC_HOME, e.g. on Ubuntu:
;        export PUREBASIC_HOME=/opt/purebasic/
;    (for details please see your system specific instructions).
;
;    If this environment variable is not set on your system, the LPP
;    executable must be in a subdirectory of the PureBasic installation
;    directory, in order to be able to handle #PB_Compiler_Home in the
;    processed code correctly.
;
;    Command line:  Lpp <Main PB source code file> <Main output file>
;
; Usage
; -----
; For using line continuation in your code, type
;     $LPP      (lower/upper case doesn't matter)
; as first executable statement in your main source code file.
;
; This tool allows to continue any line by adding a trailing _ like in
; the first example (a comment may follow). Open strings cannot be
; continued. In case that mark is part of an identifier (e.g. procedure
; or variable name), it is *not* interpreted as line continuation mark.
;
; If you want, you can define a custom string (e.g. &) as continuation
; mark for each file separately. In order to do so, write:
;     $LPP_MULTILINE = &
; on the line after $LPP in your main source code file, or as first
; executable statement in an include file. Note that the custom string
; is not enclosed in quotes.
;
; In case of an error in an include file, the IDE will display a
; temporary file with the flawed line, generated by LPP. If e.g. the
; error is on line 12 in the file "foo.pbi", in combination with LPP
; the IDE will highlight line 12 in the file "~Lpp_foo.pbi". Bear in
; mind to correct the code in the original source file (which is in the
; same directory), not in the temporary file!
;
; For working with your normal code, it's not required to switch this
; tool off in the IDE. If it does not read $LPP at the beginning of the
; source code, then the pre-processor will see that there's nothing to
; do for it and will immediately terminate.
;
; Notes
; -----
; All XincludeFile, IncludeFile, and IncludePath statements in the
; source code which is to be processed by this program
; - must not be part of multi-statemented (colon separated) lines
; - must only involve constant literal strings, #PB_Compiler_Home,
;   and #PB_Compiler_FilePath
; - must not depend on macros
;
; Include files may have different encodings.
;
; Return code of the program
; --------------------------
; 0 = source code doesn't require preprocessing
; 1 = source code successfully processed
; 2 = error (also a message is shown in this case)
;
; Credits
; -------
; This program makes use of code by Stephen Rodriguez for merging a
; Purebasic source file and all included files into a single file.
; Thank you very much!
; Thanks to all members of the German and the English Purebasic
; forums, from whom I learned tips, and who made suggestions for
; improvement of this program.
EnableExplicit
;-- CONSTANTS AND STRUCTURES
#PROGRAM_TITLE$ = "LPP 0.61"
#ABORT_MESSAGE$ = #LF$ + "Program aborted."
#TMPFILE_PREFIX$ = "~Lpp_"
#SIZEOFSTACK = 1024                        ; Max number of include files
#SLASH_CHARS$ = "/\"
#WHITESPACE$ = " " + #TAB$
#KEYWORD_SEPARATOR$ = "=" + #WHITESPACE$
#INCLUDE_SEPARATOR$ = "+" + #WHITESPACE$ + #DQUOTE$
#SYMBOLS$ = ",=-*/\&|!~()<>%:'" + #INCLUDE_SEPARATOR$
#LPP_KEYWORD$ = "$LPP"                     ; Upper case here!
#MULTILINE_KEYWORD$ = "$LPP_MULTILINE"     ;    -- " --
; Return values of PreProcessSources()
Enumeration 0 Step -1
   #LPP_NOTHINGTODO
   #LPP_OKAY
   #LPP_MAINOPENERROR
   #LPP_FILECREATEERROR
   #LPP_INVALIDCOMPILERHOME
   #LPP_INVALIDPATH
   #LPP_INVALIDINCLUDEFILE
   #LPP_NESTEDINCLUDESOVERFLOW
   #LPP_INCLUDEOPENERROR
   #LPP_INVALIDSTRINGFORMAT
   #LPP_INVALIDMULTILINESYNTAX
EndEnumeration
Structure File        ; used for the stack
   inFile$
   inFile.i
   outFile.i
   format.i
   mark$
EndStructure
;-- PLATFORM SPECIFIC CODE
CompilerSelect #PB_Compiler_OS
   CompilerCase #PB_OS_Windows
      #PB_IDE$ = "purebasic.exe"        ; file name relative to #PB_Compiler_Home
      Macro PathIsRelative (_file_)
         (Mid(_file_, 2, 1) <> ":")
      EndMacro
   CompilerCase #PB_OS_Linux
      #PB_IDE$ = "compilers/purebasic"
      Macro PathIsRelative (_file_)
         (Left(_file_, 1) <> "/")
      EndMacro
   CompilerCase #PB_OS_MacOS
      #PB_IDE$ = "PureBasic.app"
      Macro PathIsRelative (_file_)
         (Left(_file_, 1) <> "/")
      EndMacro
CompilerEndSelect
;-- GLOBAL VARIABLES
Global g_Include$      ; used internally
Global g_ErrMessage$   ; used in case of an error
;-- PROCEDURES
Procedure Help()
   Protected msg$
   msg$ = "Little Purebasic Preprocessor" + #LF$
   msg$ + "Juergen Luethje 2011" + #LF$
   msg$ + #LF$
   msg$ + "Parameters when using as IDE tool : " + #DQUOTE$ + "%FILE" + #DQUOTE$ + " "
   msg$ + #DQUOTE$ + "%COMPILEFILE" + #DQUOTE$ + " " + #DQUOTE$ + "%TEMPFILE" + #DQUOTE$ + #LF$
   msg$ + #LF$
   msg$ + "Usage as standalone program : Lpp <Main PB source code file> <Main output file>"
   MessageRequester(#PROGRAM_TITLE$, msg$)
EndProcedure
Procedure.s LTrimChars (source$, charList$=#WHITESPACE$)
   ; in : string
   ; out: string without any leading characters, that are contained in 'charList$'
   Protected left, length=Len(source$)
   left = 1
   While (left <= length) And FindString(charList$, Mid(source$,left,1), 1)
      left + 1
   Wend
   ProcedureReturn Mid(source$, left)
EndProcedure
Procedure.s RTrimChars (source$, charList$=#WHITESPACE$)
   ; in : string
   ; out: string without any trailing characters, that are contained in 'charList$'
   Protected right
   right = Len(source$)
   While (right >= 1) And FindString(charList$, Mid(source$,right,1), 1)
      right - 1
   Wend
   ProcedureReturn Left(source$, right)
EndProcedure
Procedure.s GetPBFolder()
   ; returns the absolute path of the PB installation directory
   ; (with trailing slash)
 CompilerIf #PB_Compiler_Debugger
   ; LPP runs as source code in the IDE with debugger on.
   ProcedureReturn #PB_Compiler_Home
 CompilerElse                                                  ; EXE file
   Protected folder$
   ; First look whether LPP runs as IDE tool.
   folder$ = GetEnvironmentVariable("PB_TOOLS_IDE")
   If folder$
      ProcedureReturn Left(folder$, Len(folder$)-Len(#PB_IDE$))
   EndIf
   ; Look for a regarding environment variable on the system.
   folder$ = GetEnvironmentVariable("PUREBASIC_HOME")
   If folder$
      If FindString(#SLASH_CHARS$, Right(folder$,1),1) = 0
         folder$ + "/"
      EndIf
      ProcedureReturn folder$
   EndIf
   ; LPP runs as standalone program, and the environment variable PUREBASIC_HOME is not set.
   ; In this case, the executable file of LPP must be in a SUBDIRECTORY of the PB installation directory.
   folder$ = GetPathPart(ProgramFilename())
   Repeat
      folder$ = GetPathPart(Left(folder$, Len(folder$)-1))  ; parent folder
      If folder$ = ""
         Break
      EndIf
   Until FileSize(folder$ + #PB_IDE$) > 0
   ProcedureReturn folder$
 CompilerEndIf
EndProcedure
Procedure.s ExtractCode (line$)
   ; in : source code line
   ; out: source code line without comment, and
   ;      without trailing whitespace (with a
   ;      trailing " " in case of an error, though)
   Protected char, i, length=Len(line$)
   i = 1
   While i <= length
      char = Asc(Mid(line$, i, 1))
      If char = '"'      ; skip quoted string
         i = FindString(line$, #DQUOTE$, i+1)
         If i = 0
            Break
         EndIf
      ElseIf char = 39   ; skip 'quoted number'
         i = FindString(line$, "'", i+1)
         If i = 0
            Break
         EndIf
      ElseIf char = ';'  ; end of code in given line
         line$ = Left(line$, i-1)
         Break
      EndIf
      i + 1
   Wend
   line$ = RTrimChars(line$)
   If i = 0              ; error: " or ' are unbalanced on the line
      line$ + " "
   EndIf
   ProcedureReturn line$
EndProcedure
Procedure.s GetLine (inFileID, format, cMark$, lenMark, *lineCount.Integer)
   ; in : inFileID    : input file number
   ;      format      : #PB_Ascii or #PB_UTF8
   ;      cMark$      : line continuation mark
   ;      lenMark     : length of line continuation mark
   ; out: *lineCount\i: number of blank lines to pad
   ;      return value: current whole (code) line
   Protected lenCode
   Protected line$, code$, ret$
   *lineCount\i = 0
   While Eof(inFileID) = #False
      line$ = ReadString(inFileID, format)
      code$ = ExtractCode(line$)
      lenCode = Len(code$)
      If (UCase(Right(code$,lenMark)) = cMark$) And FindString(#SYMBOLS$, Mid(code$,lenCode-lenMark,1), 1)
         If *lineCount\i = 0
            ret$ = Left(code$, lenCode-lenMark)
         Else
            ret$ + LTrimChars(Left(code$, lenCode-lenMark))
         EndIf
         *lineCount\i + 1
      ElseIf *lineCount\i > 0
         ProcedureReturn ret$ + LTrimChars(code$)
      Else
         ProcedureReturn line$
      EndIf
   Wend
   ProcedureReturn ""                      ; end of file
EndProcedure
Procedure.s NextStatement (inFileID, format, List buffer$(), *filePntr.Quad)
   ; in : inFileID    : input file number
   ;      format      : #PB_Ascii or #PB_UTF8
   ; out: buffer$()   : skipped lines
   ;      *filePntr\q : pointer of file 'inFileID' before doing ReadString()
   ;      return value: next statement in input file
   Protected line$, code$=""
   ClearList(buffer$())
   While Eof(inFileID) = #False
      *filePntr\q = Loc(inFileID)
      line$ = ReadString(inFileID, format)
      code$ = LTrimChars(ExtractCode(line$))
      If code$
         Break
      EndIf
      AddElement(buffer$())
      buffer$() = line$
   Wend
   ProcedureReturn code$
EndProcedure
Procedure.s ContinuationMark (inFileID, outFileID, format)
   ; in : inFileID : input file number
   ;      outFileID: output file number
   ;      format   : #PB_Ascii or #PB_UTF8
   ; out: custom line continuation mark,
   ;      ""  on error,
   ;      " " if #MULTILINE_KEYWORD$ statement was not found
   Protected filePntr.q, multilineKeyLength=Len(#MULTILINE_KEYWORD$)
   Protected line$, param$, ret$
   NewList buffer$()
   line$ = NextStatement(inFileID, format, buffer$(), @filePntr)
   If (UCase(Left(line$, multilineKeyLength)) = #MULTILINE_KEYWORD$) And FindString(#KEYWORD_SEPARATOR$, Mid(line$+" ", multilineKeyLength+1, 1), 1)
      param$ = LTrim(Mid(line$, multilineKeyLength+1))
      If Left(param$,1) = "="
         ret$ = UCase(LTrim(Mid(param$, 2)))         ; custom string to denote line continuation
      Else
         ret$ = ""                                   ; Syntax error
      EndIf
      AddElement(buffer$())
   Else                                              ; #MULTILINE_KEYWORD$ not found
      ret$ = " "
      FileSeek(inFileID, filePntr)                   ; rewind file pointer
   EndIf
   ForEach buffer$()
      WriteStringN(outFileID, buffer$(), format)     ; write skipped lines
   Next
   ProcedureReturn ret$
EndProcedure
Procedure.i ProcessIncludeString (inFile$, line$)
   ; in : inFile$: name of current source file
   ;      line$  : include path / filename
   ;               (can contain concatenations or
   ;                #PB_Compiler_Home, #PB_Compiler_FilePath)
   ; out: g_Include$  : string literal corresponding to line$;
   ;                    this is not necessarily a perfectly validated
   ;                    path/filename, but that will be picked up later.
   ;      return value: 0 on success, or an error code
   Protected char$, pbFolder$
   Protected length, left, right, errCode, i
   NewList token$()
   errCode = 0
   g_Include$ = ""
   
   line$ = ExtractCode(line$)
   length = Len(line$)
   If length = 0
      ProcedureReturn -2
   EndIf
   ; First we tokenise the line.
   left=1 : right=1
   Repeat
      char$ = Mid(line$, right, 1)
      If FindString(#INCLUDE_SEPARATOR$, char$, 1)
         If left < right
            AddElement(token$())
            token$() = Mid(line$, left, right-left)
            left = right
         ElseIf char$ = #DQUOTE$     ; open quote, left=right
            right = FindString(line$, char$, left+1)
            If right = 0             ; no end quote
               ClearList(token$())
               errCode = -2
               Break
            ElseIf right-left > 1
               AddElement(token$())
               token$() = Mid(line$, left, right-left+1)
            EndIf
            right+1
            left = right
         ElseIf char$ <> " "         ; left=right
            AddElement(token$())
            token$() = Mid(line$,left,1)
            left+1 : right+1
         Else
            left+1 : right+1
         EndIf
      ElseIf right = length
         right+1
         AddElement(token$())
         token$() = Mid(line$, left, right-left)
      Else
         right+1
      EndIf
   Until right > length
   ; Now process the tokens.
   i = 1
   ForEach token$()   
      If i % 2 = 0
         If token$() <> "+"
            errCode = -2
            Break
         EndIf
      ElseIf UCase(token$()) = "#PB_COMPILER_HOME"
         pbFolder$ = GetPBFOlder()
         If pbFolder$ = ""
            errCode = -1
            Break
         EndIf
         g_Include$ + pbFolder$
      ElseIf UCase(token$()) = "#PB_COMPILER_FILEPATH"
         g_Include$ + GetPathPart(inFile$)
      ElseIf Left(token$(),1) = #DQUOTE$
         g_Include$ + Mid(token$(), 2, Len(token$())-2)
      Else
         errCode = -2
         Break
      EndIf
      i + 1
   Next
   ProcedureReturn errCode
EndProcedure
Procedure.i PreProcessSources (inFile$, outFile$)
   ; -- Main procedure
   ; in : inFile$ : name of main source code file to process
   ;      outFile$: name of target file
   ; out: one of the #LPP_* constants listed at the top
   Protected inFileID, outFileID, format, stackPtr, logFileID, filePntr.q
   Protected k, inc, padLines, lenMark, processInclude, result
   Protected path$, line$, cMark$, customMark$, xInclude$, incStatement$
   Protected LogFile$ = GetTemporaryDirectory() + "~Lpp.tmp"
   ; Create file stack and line buffer
   Dim stack.File(#SIZEOFSTACK)
   stackPtr = 0                               ; number of elements on the stack
   NewList buffer$()
   ; Attempt to open the main input file
   path$ = GetPathPart(inFile$)
   inFileID = ReadFile(#PB_Any, inFile$)
   If inFileID = 0
      ProcedureReturn #LPP_MAINOPENERROR
   EndIf
   ; Identify the string encoding used in the file
   format = ReadStringFormat(inFileID)
   Select format
      Case #PB_Ascii, #PB_UTF8
      Default
         CloseFile(inFileID)
         g_ErrMessage$ = inFile$
         ProcedureReturn #LPP_INVALIDSTRINGFORMAT
   EndSelect
   ; Check the first statement of the main input file.
   If UCase(NextStatement(inFileID, format, buffer$(), @filePntr)) <> #LPP_KEYWORD$
      CloseFile(inFileID)
      ProcedureReturn #LPP_NOTHINGTODO   ; no preprocessing necessary
   EndIf
   ; Attempt to create the main output file
   outFileID = CreateFile(#PB_Any, outFile$)
   If outFileID = 0
      CloseFile(inFileID)
      g_ErrMessage$ = outFile$
      ProcedureReturn #LPP_FILECREATEERROR
   EndIf
   ; Write BOM and skipped lines to main output file.
   WriteStringFormat(outFileID, format)
   ForEach buffer$()
      WriteStringN(outFileID, buffer$(), format)
   Next
   WriteStringN(outFileID, "")
   cMark$ = "_"                              ; default line continuation mark
   ; Look for custom line continuation mark
   customMark$ = ContinuationMark(inFileID, outFileID, format)
   If customMark$ = ""
      CloseFile(inFileID)
      CloseFile(outFileID)
      g_ErrMessage$ = inFile$
      ProcedureReturn #LPP_INVALIDMULTILINESYNTAX  ; Error
   ElseIf customMark$ <> " "
      cMark$ = customMark$
   EndIf
   logFileID = CreateFile(#PB_Any, logFile$)
   If logFileID = 0
      CloseFile(inFileID)
      CloseFile(outFileID)
      g_ErrMessage$ = logFile$
      ProcedureReturn #LPP_FILECREATEERROR
   EndIf
   ; Main loop
   result = #LPP_OKAY
   Repeat
      lenMark = Len(cMark$)
      While Eof(inFileID) = #False
         line$ = GetLine(inFileID, format, cMark$, lenMark, @padLines)
         ; Check the three possible 'include' options.
         If (FindString(UCase(line$), "INCLUDEPATH",1) = 1) And FindString(#SYMBOLS$, Mid(line$,12,1),1)
            line$ = LTrimChars(RTrimChars(Mid(line$, 12)))
            If line$ = #DQUOTE$ + #DQUOTE$
               path$ = ""
            Else
               processInclude = ProcessIncludeString(inFile$, line$)
               If processInclude < 0                 ; Error
                  CloseFile(inFileID)
                  CloseFile(outFileID)
                  If processInclude = -1
                     result = #LPP_INVALIDCOMPILERHOME
                  Else
                     g_ErrMessage$ = line$
                     result = #LPP_INVALIDPATH
                  EndIf
                  Break 2
               EndIf
               path$ = g_Include$
               If path$ And (FindString(#SLASH_CHARS$, Right(path$,1),1) = 0)
                  path$ + "/"
               EndIf
            EndIf
            For k = 1 To padLines+1                        ; Write appropriate number of blank
               WriteStringN(outFileID, "")                 ; lines, in order to preserve line
            Next                                           ; numbers of subsequent statements
         Else
            inc = FindString(UCase(line$), "INCLUDEFILE",1) + 11
            If (inc = 12 Or (inc=13 And UCase(Left(line$,1))="X")) And FindString(#SYMBOLS$, Mid(line$,inc,1),1)
               incStatement$ = Left(line$, inc-1) + " "
               line$ = LTrimChars(RTrimChars(Mid(line$, inc)))
               processInclude = ProcessIncludeString(inFile$, line$)
               If processInclude < 0                        ; Error
                  CloseFile(inFileID)
                  CloseFile(outFileID)
                  If processInclude = -1
                     result = #LPP_INVALIDCOMPILERHOME
                  Else
                     g_ErrMessage$ = line$
                     result = #LPP_INVALIDINCLUDEFILE
                  EndIf
                  Break 2
               EndIf
               If PathIsRelative(g_Include$)
                  g_Include$ = path$ + g_Include$
               EndIf
               ; Write Include statement with new file name into including file.
               outFile$ = GetPathPart(g_Include$) + #TMPFILE_PREFIX$ + GetFilePart(g_Include$)
               WriteStringN(outFileID, incStatement$ + #DQUOTE$ + outFile$ + #DQUOTE$, format)
               For k = 1 To padLines                       ; Write appropriate number of blank
                  WriteStringN(outFileID, "")              ; lines, in order to preserve line
               Next                                        ; numbers of subsequent statements
               ; Check if it's OK to include the file.
               If inc = 12 Or FindString(XInclude$, "<"+g_Include$+">",1) = 0
                  ; Save information about the current file on the stack.
                  If stackPtr = #SIZEOFSTACK
                     CloseFile(inFileID)
                     CloseFile(outFileID)
                     result = #LPP_NESTEDINCLUDESOVERFLOW  ; Error
                     Break 2
                  EndIf
                  stackPtr + 1
                  stack(stackPtr)\inFile$ = inFile$
                  stack(stackPtr)\inFile  = inFileID
                  stack(stackPtr)\outFile = outFileID
                  stack(stackPtr)\format  = format
                  stack(stackPtr)\mark$   = cMark$
                  ; Attempt to open the include file.
                  inFile$ = g_Include$
                  inFileID = ReadFile(#PB_Any, inFile$)
                  If inFileID = 0
                     g_ErrMessage$ = inFile$
                     result = #LPP_INCLUDEOPENERROR        ; Error
                     Break 2
                  EndIf
                  ; Identify the string encoding used in the file.
                  format = ReadStringFormat(inFileID)
                  Select format
                     Case #PB_Ascii, #PB_UTF8
                     Default
                        CloseFile(inFileID)
                        g_ErrMessage$ = inFile$
                        result = #LPP_INVALIDSTRINGFORMAT  ; Error
                        Break 2
                  EndSelect
                  ; Attempt to create temporary output file.
                  outFileID = CreateFile(#PB_Any, outFile$)
                  If outFileID = 0
                     CloseFile(inFileID)
                     g_ErrMessage$ = outFile$
                     result = #LPP_FILECREATEERROR         ; Error
                     Break 2
                  EndIf
                  ; Write BOM to output file.
                  WriteStringFormat(outFileID, format)
                  ; Look for custom line continuation mark
                  customMark$ = ContinuationMark(inFileID, outFileID, format)
                  If customMark$ = ""
                     CloseFile(inFileID)
                     CloseFile(outFileID)
                     g_ErrMessage$ = inFile$
                     result = #LPP_INVALIDMULTILINESYNTAX  ; Error
                     Break 2
                  ElseIf customMark$ <> " "
                     cMark$ = customMark$
                     lenMark = Len(cMark$)
                  EndIf
                  ; Add this file to the list of already included files.
                  xinclude$ + "<"+inFile$+">"
                  ; Save absolute name of the temporary output file.
                  If PathIsRelative(outFile$)
                     outFile$ = GetCurrentDirectory() + outFile$
                  EndIf
                  WriteStringN(logFileID, outFile$, #PB_UTF8)
               EndIf
            Else                              ; no 'Include' command
               WriteStringN(outFileID, line$, format)
               For k = 1 To padLines                    ; Write appropriate number of blank
                  WriteStringN(outFileID, "")           ; lines, in order to preserve line
               Next                                     ; numbers of subsequent statements.
            EndIf
         EndIf
      Wend
      CloseFile(inFileID)
      CloseFile(outFileID)
      If stackPtr = 0
         Break
      EndIf
      inFile$   = stack(stackPtr)\inFile$  ; restore inFile name
      inFileID  = stack(stackPtr)\inFile   ; restore inFile ID
      outFileID = stack(stackPtr)\outFile  ; restore outFile ID
      format    = stack(stackPtr)\format   ; restore encoding
      cMark$    = stack(stackPtr)\mark$    ; restore line continuation mark
      stackPtr - 1
   ForEver
   CloseFile(logFileID)
   ; Clear the stack if there was an error.
   For k = 1 To stackPtr
      CloseFile(stack(k)\inFile)
      CloseFile(stack(k)\outFile)
   Next
   ProcedureReturn result
EndProcedure
;===========================
;-- EXECUTION ENTRY POINT
;===========================
Define ExitCode
Define Infile$, Outfile$, Msg$
If CountProgramParameters() < 2 Or CountProgramParameters() > 3
   Help()
   End 2
EndIf
Infile$ = ProgramParameter(0)        ; "%FILE"
If Infile$ = ""
   If CountProgramParameters() = 3   ; Tool in the IDE, main source code not saved
      Infile$ = ProgramParameter(2)  ; "%TEMPFILE"
   Else
      Help()
      End 2
   EndIf
EndIf
Outfile$ = ProgramParameter(1)       ; "%COMPILEFILE"
ExitCode = PreProcessSources(Infile$, Outfile$)
Select ExitCode
   ; normal termination
   Case #LPP_NOTHINGTODO
      Debug "Nothing got to do."
      End 0
   Case #LPP_OKAY
      Debug "File '" + Infile$ + "' successfully processed."
      End 1
   ; errors
   Case #LPP_MAINOPENERROR
      Msg$ = "Couldn't open main source file '" + Infile$ + "'." + #ABORT_MESSAGE$
   Case #LPP_FILECREATEERROR
      Msg$ = "Couldn't create file '" + g_ErrMessage$ + "'." + #ABORT_MESSAGE$
   Case #LPP_INVALIDCOMPILERHOME
      Msg$ = "#PB_Compiler_Home not found."
      Msg$ + #ABORT_MESSAGE$
      Msg$ + #LF$ + #LF$
      Msg$ + "Put the executable file of this program into a sub-" + #LF$
      Msg$ + "directory of the PureBasic installation directory."
   Case #LPP_INVALIDPATH
      Msg$ = "Invalid parameter for IncludePath: " + g_ErrMessage$ + "." + #ABORT_MESSAGE$
   Case #LPP_INVALIDINCLUDEFILE
      Msg$ = "Invalid parameter for XIncludeFile or IncludeFile: " + g_ErrMessage$ + "." + #ABORT_MESSAGE$
   Case #LPP_NESTEDINCLUDESOVERFLOW
      Msg$ = "Too many nested includes" + #LF$
      Msg$ + "(probably a recursive use of a certain include file)." + #ABORT_MESSAGE$
   Case #LPP_INCLUDEOPENERROR
      Msg$ = "Couldn't open include file '" + g_ErrMessage$ + "'." + #ABORT_MESSAGE$
   Case #LPP_INVALIDSTRINGFORMAT
      Msg$ = "Unsupported encoding in source file '" + g_ErrMessage$ + "'." + #ABORT_MESSAGE$
   Case #LPP_INVALIDMULTILINESYNTAX
      Msg$ = "Invalid syntax for " + #MULTILINE_KEYWORD$ + " in file '" + g_ErrMessage$ + "'." + #ABORT_MESSAGE$
EndSelect
MessageRequester(#PROGRAM_TITLE$, Msg$)
End 2Code: Select all
; -- LppClean: Companion program for LPP
; -- Version 0.60, 2009-02-28
; Public Domain, written by Juergen Luethje <http://luethje.eu/>.
; Developed with Purebasic 4.30.
; Cross-platform, Unicode compliant.
; Tested on Windows XP, and Ubuntu 8.10.
; Standard disclaimer: USE AT YOUR OWN RISK!
;
; Purpose
; -------
; Delete temporary files that are generated by LPP.
;
; Installation
; ------------
; Compile the program to an EXE file.
;
; In the IDE, choose from the menu "Configure Tools ...".
; In the window that appears, add the program 2 times with
; different names by clicking the [ New ] button.
;
; Use as trigger
; - for one name      : After Compile/Run
; - for the other name: After Create Executable
;
; and for both names choose the setting
;
; [v] Hide Tool from the Main menu
EnableExplicit
Define LogFile$, Line$
Define LogFileID, Ignore
LogFile$ = GetTemporaryDirectory() + "~Lpp.tmp"
LogFileID = ReadFile(#PB_Any, LogFile$)
If LogFileID
   Ignore = #False
   While Eof(LogFileID) = #False
      Line$ = ReadString(logFileID, #PB_UTF8)
      If DeleteFile(Line$) = 0 And Ignore = #False
         If MessageRequester("LppClean", "Couldn't delete file '" + Line$ + "'." + #LF$ + "Ignore such problems?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
            Ignore = #True
         EndIf
      EndIf
   Wend
   CloseFile(LogFileID)
   DeleteFile(LogFile$)
EndIfCode: Select all
; Demo program 1 for LPP.
; The possibility to write 1 statement on multiple lines allows to view
; long statements without horizontal scrolling. As shown in the example,
; this is also useful e.g. for nicely adding a comment to each parameter
; of a procedure.
$Lpp                                     ; comment
Procedure Demo (ParameterA.l, _          ; cool parameter
                ParameterB.w, _          ; important parameter
                ParameterC.s, _          ; special parameter
                ParameterD.c)            ; another parameter
   Debug ParameterA + ParameterB         ; Line numbers of subsequent
   Debug "ASCII of the semicolon is:"    ; statements remain unchanged.
   Debug ';'
   a$ = "Hello " _
      + "world!"
   Debug a$
EndProcedure
Demo(7,3,"",0)Code: Select all
; File "animals.pbi"
$Lpp_Multiline = To be continued ...
Animal1$ To be continued ...
   = "dog"
Animal2$ To be continued ...
   = "cat"Code: Select all
; Demo program 2 for LPP.
; The possibility to write 1 statement on multiple lines allows to view
; long statements without horizontal scrolling.
$Lpp
XIncludeFile _                            ; Include file
   #PB_Compiler_FilePath + "animals.pbi"  ; with pets.
Procedure Demo (ParameterA.l, _           ; cool parameter
                ParameterB.w, _           ; important parameter
                ParameterC.s, _           ; special parameter
                ParameterD.c)             ; another parameter
   Debug ParameterA + ParameterB          ; Line numbers of subsequent
   Debug "ASCII of the semicolon is:"     ; statements remain unchanged.
   Debug ';'
   a$ = "Hello " _
      + "world!"
   Debug a$
EndProcedure
Demo(7,3,"",0)
Debug Animal1$
Debug Animal2$


