In some situations an extended pre compiler support would be useful. I specially missed CompilerGoto, CompilerSet and labels, these additional functions would allow iterativ code line generation.
As the PB Editor greatly allows the integration of your own pre processor, I have written a tiny one.
It runs through the PB source and expands certain pre processor statements and creates a new temporary PB file which is passed to the PB compiler.
It supports the following statements:
Code: Select all
.SET variable value/variable ; to set/define a variable
.ADD variable value/variable ; to add a value to a variable
.SUB variable value/variable ; to subtract a value
.IF variable operator value/variable label ; to compare a variable an goto to a label
.LABEL label ; to define a label
.GOTO label ; to branch to a label
.DOFOR variable start-value endvalue ; counted loop
.ENDDO ; terminate a loop
To give you an example, I generate several debug statements (it's a silly sample granted, but illustrates the usage):
Code: Select all
.set maxc 10
; Create loop to generate the DEBUG statement 10 times (maxc)
.set myCounter 0
.Label LOOP1 ; this is our loop label
Debug "Generated Statement: "+Str(@myCounter) ; this line should be duplicated 10 times, the @ sign identifies a variable to be substituted
.ADD myCounter 1 ; add 1 to our loop counter
.If mycounter <= maxc loop1 ; goto loop1 until maxc is reached
Code: Select all
; count-variable
; ! from value
; ! ! to value
; ! ! !
.DOFOR nextCounter 0 maxc ; repeat loop 10 times (maxc)
Debug "Generated with DOFOR: "+Str(@nextCounter)
.enddo ; end of DOFOR loop
1 means show results in NOTEPAD and wait for QUIT
2 means show results in NOTEPAD and continue immediately
0 means, don't show result (you can also drop the the set)
Code: Select all
.set _DISPLAY_STEP1 1 ; show result of pre processor level 1
.set _DISPLAY_STEP2 1 ; show result of pre processor level 2 (to be compiled version)
.set _DISPLAY_TRACE 1 ; show trace log of pre processor execution
parameter 1: input-file
parameter 2: output-file
Or you add it to the tools and integrate it into the compile (description is in the coding).
The syntax checks of the pre processor statements are just basic.
The coded was developed and tested with PB 4.50
Code: Select all
Macro THEN(stmt)
: stmt
EndIf
EndMacro
;
#max_iterations =1000 ; maximum iterations per label (avoid endless loops)
#pre_escape ="." ; Pre Processor Escape char, to identify PreProc statement
#pre_Var_identifier="@" ; Prefix to identify pre processor variable in non pre processor lines for substitution
#goto_stmt =#pre_escape+"GOTO"
#set_stmt =#pre_escape+"SET"
#add_stmt =#pre_escape+"ADD"
#sub_stmt =#pre_escape+"SUB"
#if_stmt =#pre_escape+"IF"
#dofor_stmt =#pre_escape+"DOFOR"
#enddo_stmt =#pre_escape+"ENDDO"
#label_stmt =#pre_escape+"LABEL"
; --------------------------------------------------------------------
; Installation:
; 1. Create Exe: e.g. PreProcessor.exe from source
; 2. Integrate in Compile/Run in Editor
; --------------------------------------------------------------------
; Add to Tools
; ------------
; Tools -> Configure Tools -> New:
;
; Commandline: c:\.....\...\PreProcessor.exe
; Arguments : "%file" "%compilefile"
;
; Name : PreProcessorRun
; Event : Before Compile/Run
;
; WAIT Until tool quits : x
; Run Hidden : x
; Enable Tool on a pre-source basis: x
;
;Tools -> Configure Tools -> New:
;
; Commandline: c:\.....\...\PreProcessor.exe
; Arguments : "%file" "%compilefile"
;
; Name : PreProcessorCompile
; Event : Before Create Executable
;
; WAIT Until tool quits : x
; Run Hidden : x
; Enable Tool on a per-source basis: x
;
;
; Activate in Compile Options
; ---------------------------
; Compiler -> Compiler Options -> Tab: Compile / Run
;
; Execute Tools : x PreProcessorRun
; x PreProcessorCompile
;
; ----------------------------------------------------------------------------------------------
;- Macros
; ----------------------------------------------------------------------------------------------
Macro ThenElse(tstmt,estmt)
: tstmt : Else : estmt :EndIf
EndMacro
Macro AddItem(LList,ltext)
AddElement(LList#())
llist#()=ltext
EndMacro
; ----------------------------------------------------------------------------------------------
;- Declares
; ----------------------------------------------------------------------------------------------
Declare ErrorMSG(type,msg.s)
Declare ReadIntoList(infile$)
Declare MacExpand(infile$,outFile$)
Declare Notepad(ofile$,waitflag)
Declare DisplayList(file$,List inlist.s(),intype.s,numbering,waitflag)
; ----------------------------------------------------------------------------------------------
;- Global Defintions
; ----------------------------------------------------------------------------------------------
Global NewMap MacLabel(), NewMap MacLabelUsed(), NewMap MacVariable.s(), NewList MacGoto.s()
Global NewList Stack.s(), NewList Trace.s(), NewList Source.s()
Global Dim words.s(10), lino, ddname, suppressExpandlog
Global EoL.s="!!", sysctr, infile$, outfile$, OutfileToken
*ptr.byte=@EOL
*ptr\b=#LF
*ptr+1
*ptr\b=#LF
; ==============================================================================================
;- Main Procedure
; ==============================================================================================
Procedure Main()
Infile$ = ProgramParameter(0)
Outfile$ = ProgramParameter(1)
If infile$="" then(infile$=OpenFileRequester("Select File fpr PB Precompile","","*",0))
If Outfile$="" then(outfile$=OpenFileRequester("Select Generated Output FileFile","","*",0))
; Check input file parameter
If Infile$ = ""
ErrorMSG(1,"PB Source File definition missing")
End
EndIf
; Check output file parameter
If Outfile$ = ""
ErrorMSG(1,"PB Output File definition missing")
End
EndIf
ReadIntoList(infile$)
MacExpand(infile$,outFile$)
CopyFile(outfile$,GetPathPart(outfile$)+GetFilePart(outfile$,#PB_FileSystem_NoExtension)+"_PRC."+GetExtensionPart(outfile$))
trace=Val(macVariable("_DISPLAY_TRACE"))
step2=Val(macVariable("_DISPLAY_STEP2"))
precomp$="Precompile Trace"
If trace=1 then(precomp$=precomp$+", process halted until NOTEPAD closed")
If trace=2 then(precomp$=precomp$+", process continued while NOTEPAD open")
If trace>0 then(DisplayList(GetPathPart(outfile$)+"TRC_"+GetFilePart(outfile$),trace(),precomp$,0,trace))
If step2>0 then(Notepad(outFile$,step2))
EndProcedure
; ----------------------------------------------------------------------------------------------
;- Supporting sub Procedures
; ----------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------
; Error Message handling
; ----------------------------------------------------------------------------------------------
Procedure ErrorMSG(type,msg$)
If type=1
rc=MessageRequester("Macro Expand", msg$+#LF$+" Pre Processing terminated",#PB_MessageRequester_Ok)
End
ElseIf type=2
If IsFile(ddname) then(CloseFile(ddname))
If IsFile(OutfileToken) then(CloseFile(OutfileToken))
rc=MessageRequester("Macro Expand", "line: "+Str(lino)+" "+msg$+#LF$+" View created code?",#PB_MessageRequester_YesNo)
If rc=#PB_MessageRequester_No then(End)
NotePad(outFile$,1)
EndIf
End
EndProcedure
; ----------------------------------------------------------------------------------------------
; Output Entire LIST
; ----------------------------------------------------------------------------------------------
Procedure DisplayList(file$,List inlist.s(),intype.s,numbering,waitflag)
File=CreateFile(#PB_Any,file$)
inlen=Len(intype)
WriteStringN(File,"; "+LSet("=",inlen,"="))
WriteStringN(File,"; "+intype)
WriteStringN(File,"; "+LSet("=",inlen,"="))
dlino=0
ForEach inlist()
dlino+1
If numbering=1
WriteStringN(File,RSet(Str(dlino),4,"0")+" "+inlist())
Else
WriteStringN(File,inlist())
EndIf
Next
CloseFile(File)
NotePad(file$,waitflag)
EndProcedure
; ----------------------------------------------------------------------------------------------
; Show Output or Trace file in Notepad
; ----------------------------------------------------------------------------------------------
Procedure Notepad(ofile$,waitflag)
If waitflag=0 then(ProcedureReturn)
If waitflag=1 thenElse(waitflag=#PB_Program_Wait,Waitflag=0)
RunProgram("Notepad++.exe", oFile$,"",waitflag)
EndProcedure
; ----------------------------------------------------------------------------------------------
; Log Pre-Processing Actions
; ----------------------------------------------------------------------------------------------
Procedure ExpandLog(clino,msg$)
If suppressExpandlog=1 then(ProcedureReturn 0)
trace=Val(macVariable("_DISPLAY_TRACE"))
If trace=0 then(ProcedureReturn)
AddItem(Trace,RSet(Str(clino),4,"0")+msg$)
EndProcedure
; ----------------------------------------------------------------------------------------------
; Save value in LIFO Stack
; ----------------------------------------------------------------------------------------------
Procedure PushM(stack_value.s)
AddElement(Stack())
Stack()=stack_value
EndProcedure
; ----------------------------------------------------------------------------------------------
; Retrieve Last Entry from LIFO Stack
; ----------------------------------------------------------------------------------------------
Procedure.s PullM()
If ListSize(stack())=0 then(ProcedureReturn "")
value.s=Stack()
DeleteElement(stack())
ProcedureReturn value
EndProcedure
; ----------------------------------------------------------------------------------------------
; Check Datatype of string
; ----------------------------------------------------------------------------------------------
Procedure DataType(input.s)
numeric=Val(input)
If input=Str(numeric) then(ProcedureReturn 1)
ProcedureReturn 0
EndProcedure
; ----------------------------------------------------------------------------------------------
; Return max length of 2 values
; ----------------------------------------------------------------------------------------------
Procedure maxlen(s1.s,s2.s)
i1=Len(s1)
i2=Len(s2)
If i1>i2 then(ProcedureReturn i1)
ProcedureReturn i2
EndProcedure
; ----------------------------------------------------------------------------------------------
; Find Next Blank in Line
; ----------------------------------------------------------------------------------------------
Procedure NextBlank(line$,spos)
llen=Len(line$)
If spos<1 then(spos=1)
If spos>llen then(ProcedureReturn llen+1) ; string ends without blank, +1 for calculation
For i=spos To llen
If Mid(line$,i,1)=" " then(ProcedureReturn i)
Next
ProcedureReturn llen+1
EndProcedure
; ----------------------------------------------------------------------------------------------
; Find Next non Blank Character in Line
; ----------------------------------------------------------------------------------------------
Procedure NextChar(line$,spos)
llen=Len(line$)
If spos<1 then(spos=1)
If spos>llen then(ProcedureReturn 0)
For i=spos To llen
If Mid(line$,i,1)<>" " then(ProcedureReturn i)
Next
ProcedureReturn 0
EndProcedure
; ----------------------------------------------------------------------------------------------
; Retrieve Numeric Value from provided String or resolve string as numeric Variable
; ----------------------------------------------------------------------------------------------
Procedure GetVarOrValueN(item$)
value=Val(item$)
If DataType(item$)=0 ; string is alpha, but maybe a variable
valstr$=macVariable(item$)
If valstr$="" then(ErrorMSG(2,item$+" Variable not defined"))
value=Val(valstr$)
EndIf
ProcedureReturn value
EndProcedure
; ----------------------------------------------------------------------------------------------
; Retrieve Numeric Value from provided String or resolve string as numeric Variable
; ----------------------------------------------------------------------------------------------
Procedure.s GetVarOrValueS(item$)
value.s=macVariable(item$)
If value="" then(value=item$)
ProcedureReturn value
EndProcedure
; ----------------------------------------------------------------------------------------------
; Parse Parameters to Array: Words
; ----------------------------------------------------------------------------------------------
Procedure Parse(line$)
If Mid(line$,1,1)=" " thenElse(wbeg=NextChar(line$,1),wbeg=1)
wi=0
For i=0 To 10
words(i)=""
Next
Repeat
Wrend=NextBlank(line$,wbeg+1)
words(wi)=Mid(line$,wbeg,Wrend-wbeg)
wi+1
wbeg=NextChar(line$,wrend+1)
Until wbeg=0 Or wi>10
EndProcedure
; ----------------------------------------------------------------------------------------------
; Save label and its line number (will be used by GOTO)
; ----------------------------------------------------------------------------------------------
Procedure fetchLabel(whereTOgo)
label.s=words(1)
If Mid(label,1,1)="." then(label=Mid(label,2))
If label=""
ErrorMSG(2,"Label Not found: "+label)
Else
MacLabel(label)=WhereToGo
EndIf
ProcedureReturn linenumber
EndProcedure
; ----------------------------------------------------------------------------------------------
; Save GOTO for later label check
; ----------------------------------------------------------------------------------------------
Procedure fetchGOTO(label.s)
If Mid(label,1,1)="." then(label=Mid(label,2))
If label=""
ErrorMSG(2,"Label Not found: "+label)
Else
AddItem(MacGoto,label)
EndIf
ProcedureReturn linenumber
EndProcedure
; ----------------------------------------------------------------------------------------------
;- Precompiler Statements
; ----------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------
; Substitute pre-Proc variables in normal PB line
; ----------------------------------------------------------------------------------------------
Procedure.s substitute(line$)
If FindString(line$,#pre_Var_identifier,1)=0 then(ProcedureReturn line$)
ResetMap(MacVariable())
ForEach MacVariable()
varname.s=MapKey(MacVariable())
varcontent.s=Macvariable(varname)
line$=ReplaceString(line$,#pre_Var_identifier+varname,varcontent,#PB_String_NoCase)
Next
ProcedureReturn line$
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor GOTO
; ----------------------------------------------------------------------------------------------
Procedure _goto()
curlino=lino
label.s=words(1)
If Mid(label,1,1)="." then(label=Mid(label,2))
MacLabelUsed(label)=MacLabelUsed(label)+1
If MacLabelUsed(label)>#max_iterations
ErrorMSG(2,"Maximum iteration count ("+Str(#max_iterations)+") exceeded For Label "+label)
ProcedureReturn -1
EndIf
lino=MacLabel(label)
ExpandLOG(Curlino,"+++ .GOTO "+Str(lino))
If lino=0
ErrorMSG(2,"Label "+label+" not found")
Else
SelectElement(Source(),lino-2) ; -1 for rel. line, -1 to set to line before, as NEXTITEM will read next entry
EndIf
ProcedureReturn 0
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor .SET
; ----------------------------------------------------------------------------------------------
Procedure _set()
curlino=lino
variable.s=words(1)
value.s=words(2)
oldval.s=macVariable(variable)
If Trim(oldval)<>"" then(oldval=", OLD VALUE: "+oldval)
macVariable(variable)=value
ExpandLOG(Curlino,"+++ .SET to "+value+oldval)
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor .ADD
; ----------------------------------------------------------------------------------------------
Procedure _add()
curlino=lino
variable.s=words(1)
value=GetVarOrValueN(words(2))
If datatype(macVariable(variable))=0
ErrorMSG(2,variable+" is not numeric: "+Str(value))
ProcedureReturn -1
EndIf
oldval=Val(macVariable(variable))
curval=oldval+value
macVariable(variable)=Str(curval)
ExpandLOG(Curlino,"+++ .ADD "+Str(oldval)+" + "+Str(value)+" = "+Str(curval))
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor .SUB (Subtract)
; ----------------------------------------------------------------------------------------------
Procedure _sub()
curlino=lino
variable.s=words(1)
value=GetVarOrValueN(words(2))
If datatype(macVariable(variable))=0
ErrorMSG(2,variable+" is not numeric: "+Str(value))
ProcedureReturn -1
EndIf
oldval=Val(macVariable(variable))
curval=oldval-value
macVariable(variable)=Str(curval)
ExpandLOG(Curlino,"+++ .SUB "+Str(oldval)+" - "+Str(value)+" = "+Str(curval))
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor .IF variable1 operator variable2/value label
; ----------------------------------------------------------------------------------------------
Procedure _if()
curlino=lino
comp1.s=words(1)
operator.s=words(2)
comp2.s=words(3)
label.s=words(4)
If Mid(label,1,1)="." then(label=Mid(label,2))
compv1.s=macVariable(comp1)
If compv1="" then(compv1=comp1)
compv2.s=macVariable(comp2)
If compv2="" then(compv2=comp2)
; select compare type Numeric, or Alpha
ctype=1 ; numeric compare standard
If datatype(compv1)=0 Or datatype(compv2)=0 then(ctype=0) ; must be Alpha compare
If ctype=1
clen=maxlen(compv1,compv2)
compv1=RSet(compv1,clen)
compv2=RSet(compv2,clen)
EndIf
operation=0
ExpandLOG(Curlino,"+++ .if "+compv1+" "+operator+" "+compv2)
Select operator
Case ">"
If compv1>compv2 then(operation=1)
Case "<"
If compv1<compv2 then(operation=1)
Case "="
If compv1=compv2 then(operation=1)
Case ">="
If compv1>=compv2 then(operation=1)
Case "<="
If compv1<=compv2 then(operation=1)
Case "<>"
If compv1<>compv2 then(operation=1)
Default
ErrorMSG(2,".IF "+words(1)+" "+words(2)+" "+words(3)+" in Error")
ProcedureReturn -1
EndSelect
If operation=1
MacLabelUsed(label)=MacLabelUsed(label)+1
If MacLabelUsed(label)>#max_iterations
ErrorMSG(2,"Maximum iteration count ("+Str(#max_iterations)+") exceeded For Label "+label)
ProcedureReturn -1
EndIf
lino=MacLabel(label)
If lino=0
ErrorMSG(2,"Label "+label+" not found")
Else
SelectElement(Source(),lino-2) ; -1 for rel. line, -1 to set to line before, as NEXTITEM will read next entry
ExpandLOG(Curlino,"+++ .(IF) GOTO "+Str(lino))
EndIf
EndIf
ProcedureReturn operation
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor DOFOR
; ----------------------------------------------------------------------------------------------
Procedure _dofor(curlino)
variable.s=words(1)
from.s=GetVarOrValueS(words(2))
upto.s=GetVarOrValueS(words(3))
macVariable(variable)=from
label.s=Str(sysctr)
MacLabel("BEGIN_"+label)=curlino+2 ; set to ADD statement
from=Str(Val(from)-1)
stmt.s=".dofor "+variable+"="+from+" TO "+upto
source()="; >>>>> "+stmt
AddItem(Source,".set "+variable+" "+from )
AddItem(Source,".LABEL Begin_"+label )
AddItem(Source,".add "+variable+" 1")
AddItem(Source,".if "+variable+" > "+upto+" .end_"+label)
AddItem(Source,"; <<<<< "+stmt)
fetchGoto(".BEGIN_"+label)
fetchGoto(".END_"+label)
PUSHM(label)
EndProcedure
; ----------------------------------------------------------------------------------------------
; pre-Processsor ENDDO
; ----------------------------------------------------------------------------------------------
Procedure _enddo(curlino)
label.s=PULLM()
source()="; >>>>> .enddo"
AddItem(Source,".goto .Begin_"+label)
AddItem(Source,".LABEL .End_"+label)
AddItem(Source,"; <<<<< .enddo")
MacLabel("END_"+label)=curlino+2 ; set +2 lines to .LABEL Statement
ProcedureReturn 0
EndProcedure
; ----------------------------------------------------------------------------------------------
; Get Next Record from Source List
; ----------------------------------------------------------------------------------------------
Procedure.s GetNext()
If NextElement(Source())=0 then(ProcedureReturn EOL)
ProcedureReturn Source()
EndProcedure
; ----------------------------------------------------------------------------------------------
;- Macro Expand Procedures
; ----------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------
; Step 1: Read File into List and pre - pre Process
; ----------------------------------------------------------------------------------------------
Procedure ReadintoList(infile$)
suppressExpandlog=1
Inf=ReadFile(#PB_Any,infile$,#PB_File_SharedRead|#PB_File_SharedWrite)
ReadStringFormat(inf)
If inf=0 then(ErrorMSG(1,infile$+" not available"))
sysctr=0
While Eof(inF) = #False
Line$=ReadString(inf,#PB_Ascii)
AddItem(Source,line$)
mline$=UCase(Trim(line$))
If Mid(mline$,1,1)<>#pre_escape then(Continue)
linenumber=ListIndex(Source())+1 ; set to line number (=+1)
sysctr+1
Parse(mline$)
If words(0)=#label_stmt then(fetchLabel(linenumber)) ; save line number of label
If words(0)=#dofor_stmt then(_dofor(linenumber)) ; expand For statement now
If words(0)=#enddo_stmt then(_enddo(linenumber)) ; expand For statement now
If words(0)=#goto_stmt then(fetchGoto(words(1))) ; save Gotos for syntax check
If words(0)=#if_stmt then(fetchGoto(words(4))) ; save Gotos for syntax check
If words(0)=#set_stmt And Mid(words(1),1,1)="_" then(_set()) ; set, temporary for Precompile control sets
Wend
CloseFile(inf)
ResetList(Source())
lino=0
step1=Val(macVariable("_DISPLAY_STEP1"))
pretxt$="Before Pre Compile, this source will be analysed and expanded"
If step1=1 then(pretxt$=pretxt$+", process halted until NOTEPAD closed")
If step1=2 then(pretxt$=pretxt$+", process continued while NOTEPAD open")
If step1>0 then(DisplayList(GetPathPart(outfile$)+"PRE_"+GetFilePart(outfile$),source(),pretxt$,1,step1))
ForEach MacGoto()
testLabel.s=MacGoto()
If MacLabel(testLabel)=0 then(Errormsg(2,testLabel+" Label not defined"))
Next
ProcedureReturn linenumber
EndProcedure
; ----------------------------------------------------------------------------------------------
; Step 2: Expand entire PB source file
; ----------------------------------------------------------------------------------------------
Procedure MacExpand(infile$,outfile$)
suppressExpandlog=0
ddname=OpenFile(#PB_Any,infile$)
outFileToken=CreateFile(#PB_Any,outfile$)
If outFileToken=0
ErrorMSG(1,"Error: can't write into :"+outfile$)
End
EndIf
;
; Prepare final pre processor output file
pretxt$="Pre compiled Source, will be sent to PB Compiler"
step2=Val(macVariable("_DISPLAY_STEP2"))
If step2=1 then(pretxt$=pretxt$+", process halted until NOTEPAD closed")
If step2=2 then(pretxt$=pretxt$+", process continued while NOTEPAD open")
WriteStringN(outFileToken,"; "+LSet("=",Len(pretxt$),"="))
WriteStringN(outFileToken,"; "+pretxt$)
WriteStringN(outFileToken,"; "+LSet("=",Len(pretxt$),"="))
WriteStringN(outFileToken,"; Pre Compiled on: "+FormatDate("%dd.%mm.%yyyy", Date())+" "+FormatDate("%hh:%ii:%ss", Date()))
;
; Loop through all lines of expanded input file
linc=ListSize(Source())
ResetList(Source())
;
Repeat
Line$=GetNext()
lino=ListIndex(Source())+1
If line$=EOL then(Break)
mline$=UCase(Trim(line$))
If mline$="" then(Continue)
If Mid(mline$,1,1)=";" then(Continue)
ExpandLOG(lino," "+line$) ; write record to log
; identify pre processor line/normal line
If Mid(mline$,1,1)<>#pre_escape
line$=substitute(line$) ; it's a normal line, try to substitute variables
ExpandLOG(lino,"+++ "+line$)
WriteStringN(outFileToken,line$)
Continue
EndIf
; pre processor line (1. char = ".")
Parse(mline$)
If words(0)=#goto_stmt
_goto()
ElseIf words(0)=#set_stmt
_set()
ElseIf words(0)=#if_stmt
_if()
ElseIf words(0)=#add_stmt
_add()
ElseIf words(0)=#sub_stmt
_sub()
ElseIf words(0)=#label_stmt
Continue
ElseIf words(0)=#dofor_stmt
Continue
ElseIf words(0)=#enddo_stmt
Continue
Else
; unknown pre processor statement, treat it as normal line
line$=substitute(line$) ; normal line try substitute
WriteStringN(outFileToken,line$)
EndIf
Until line$=EOL
CloseFile(ddname)
CloseFile(outFileToken)
EndProcedure
; =========================================================================================================================
; Run Main Code in Procedure
; =========================================================================================================================
main()
Peter