Page 1 of 2
coding challenge - codecaddy multiline
Posted: Fri Dec 09, 2005 9:31 pm
by blueznl
so, a little coding challenge... see here why... yeah, wanna let you do the work
viewtopic.php?t=18133&start=15
the challenge is:
- create some routine that turns 'multiline' purebasic code into single line
- purebasic code, using purebasic only (no asm)
- each line is considered part of a multiline if it ends on [space][underscore], OR
- each line may have a comment after a [space][underscore][space] sequence
- after processing the total number of lines should be the same
- your code may strip the comments
- your code does not need to handle colon's (but it may)
- your code must accept single and double parenthesis ' and "
for bonus points you're allowed to make the 'comments allowed rule' (number 4 above) optional, ie. add a flag to do so or not
who writes the nicest / fastest routine?
and the price? euh... my gratefullness? your name mentioned in codecaddy? or just a general feeling of being good?
example:
Code: Select all
Debug 2+2+ _ ; this is the first line
2 ; and this is the second
should be turned into
example:
Code: Select all
OpenWindow( #1,10,10, 100,100, _ ; line 1
#PB_Window_SystemMenu, _ ; line 2
"test") ; line 3 contains the title 'test'
should be turned into:
Code: Select all
OpenWindow( #1,10,10,100,100, #PB_Window_SystemMenu, "test")
(added)
here's a file to process (just copy it and save it, then use your code to process it)
Code: Select all
Debug '_' + _
'_' ; this is not as easy
Debug '"' + _ ; as it looks
'a'
Debug "'" + "a" + "'" ; ah, the joys of coding
Debug Chr('_') _
; can make life so sweet
Debug "_ is the same as " _
+ Chr('_')
a.l = 1 + _ ; multiline ahoy
2 + _
'b' + _
Asc("c")
;
w_main_nr = OpenWindow(#PB_Any, _
10,10, _
100,100, _
#PB_Window_SystemMenu, _
"Test")
Repeat
event = WaitWindowEvent()
Until event = #PB_Event_CloseWindow
End
Re: coding challenge - codecaddy multiline
Posted: Fri Dec 09, 2005 9:50 pm
by PB
Here's the code I wrote years ago to do it, but doesn't take comments at the
end of lines into account... should be easy enough to modify it though. It
works exactly the same way as Visual Basic does it (which also doesn't
take comments at end of lines into account):
Code: Select all
; Source code line continuation code example by PB.
; Any line ending with " _" carries on to the next line.
; An example of the syntax:
a$="This will comp _
ile into one sing _
le line of code"
o$="old.pb" : n$="new.pb"
;
If ReadFile(0,o$)
CreateFile(1,n$)
Repeat
UseFile(0)
a$=ReadString()
While Right(a$,2)=" _"
l$+Left(a$,Len(a$)-2)
a$=LTrim(ReadString()) ; LTrim in case continued line was indented.
Wend
UseFile(1)
If l$<>""
WriteStringN(l$+a$)
l$=""
Else
WriteStringN(a$)
EndIf
Until Eof(0)<>0
CloseFile(0)
CloseFile(1)
EndIf
Posted: Fri Dec 09, 2005 10:36 pm
by Trond
There are no comments in this one either. It does lines which ends with whitespace after " _" too, unlike PB's version. It's also quite a bit faster. It also keeps the correct number of lines in the file so that error reports on subsequent lines will not come out gonzo (this too unlike PB's version).
Edit: NEVER MIND WAIT A BIT I FORGOT STRINGS!
Code: Select all
Procedure Apple()
ReadFile(0, "in.txt")
Mem = AllocateMemory(Lof()+10)
Ptr = 0
Lin = 0
While Eof(0) = 0
Lin = 0
s.s = ReadString()
For i=0 To Len(s)-1
If PeekB(@s+i) = ' '
If PeekB(@s+i+1) = '_'
s = RTrim(s)
s = Left(s, Len(s)-2)
s + Trim(ReadString())
Lin + 1
EndIf
EndIf
Next
PokeS(Mem+Ptr, s.s)
Ptr + Len(s)
Repeat
PokeS(Mem+Ptr, #CRLF$)
Ptr + 2
Lin - 1
Until Lin = -1
Wend
CloseFile(0)
CreateFile(0, "out.txt")
WriteData(Mem, Ptr)
Posted: Fri Dec 09, 2005 10:43 pm
by PB
> It does lines which ends with whitespace after " _" too, unlike PB's version
So, just change my line of code from a$=ReadString() to a$=RTrim(ReadString())
> It's also quite a bit faster
Yep, because it does all the work in memory instead of dual file-access.
As I said, it was written years ago, just as a proof-of-concept. It wasn't
meant to be a final release or anything.
> It also keeps the correct number of lines in the file so that error reports
> on subsequent lines will not come out gonzo (this too unlike PB's version)
The concept of mine, as you can see, was to recreate the source, thus any
line numbering issues are totally irrelevant and not an issue.

Posted: Fri Dec 09, 2005 10:51 pm
by Trond
But yours actually WORKS and mine doesn't...
Posted: Fri Dec 09, 2005 10:55 pm
by PB
I just tried yours and it seems fine?
Posted: Fri Dec 09, 2005 10:58 pm
by Trond
It messes up with _'s inside strings.
Posted: Fri Dec 09, 2005 11:04 pm
by PB
Ah, I see.
BTW, I just tried Visual Basic and note that it doesn't allow multi-line strings
using space+underscore either; the following VB code gives a syntax error
for the definition of a$, but the Debug.Print line works as expected:
Code: Select all
Private Sub Command1_Click()
a$="hello _
there"
Debug.Print _
a$
End Sub
Also, regarding the issue of the compiler reporting errors on "the wrong
line" -- is this really an issue? Both Visual Basic and PureBasic don't
actually use line numbers... all that matters is the incorrect line gets
highlighted with the error...
Posted: Sat Dec 10, 2005 12:29 am
by blueznl
yes it is, it should mark the right line
regarding long 'strings' as in
it's a bit of a choice, either allow comments behind the _ or allow overlong strings, i prefer comments personally
Posted: Sat Dec 10, 2005 1:07 am
by blueznl
my attempt
uncomment the debug's to see it's output, this one does comments, and makes no mistakes on _ in strings
Code: Select all
Dim source.s(100)
ReadFile(1,"challenge.pb")
n = 0
While Eof(1) = 0
source.s(n) = ReadString()
n = n+1
Wend
CloseFile(1)
t1 = GetTickCount_()
For nnn = 1 To 10000
y.s = ""
For nn = 0 To n-1
x.s = source.s(nn)
;
x_p = @x
l = Len(x) ; length of string
t = 0 ; test flag, 1 is normal line, 2 is multiline
p = 0 ; counter / pointer
;
If l < 3 Or FindString(x.s," _",1) = 0 ; findstring is faster than my code
t = 1
Else
b = PeekB(x_p+p)
Repeat
If b = 34 ; double quote char
Repeat ; repeat until another one is hit
p = p+1
b = PeekB(x_p+p)
Until b = 34 Or p = l-2
p = p+1
b = PeekB(x_p+p)
ElseIf b = 39 ; single quote char
Repeat ; repeat until another one is hit
p = p+1
b = PeekB(x_p+p)
Until b = 39 Or p = l-2
p = p+1
b = PeekB(x_p+p)
ElseIf b = ' ' ; ah a space
Repeat
p = p+1
b = PeekB(x_p+p)
Until b <> ' ' Or p = l-1 ; keep on going until it's no longer a space
If b ='_'
If p = l-1
t = 2 ; we're on an eol with a [space][underscore]
p = p-1 ; adjust pointer to spot before [underscore]
Else
p = p+1
b = PeekB(x_p+p)
If b = ' '
t = 3 ; we just passed a [space][underscore][space]
p = p-2 ; adjust pointer to spot before [underscore]
EndIf
EndIf
EndIf
ElseIf b = ';' Or b = ':'
t = 1
ElseIf p < l-1
p = p+1
b = PeekB(x_p+p)
Else
t = 1
EndIf
Until t <> 0
EndIf
;
If t=1
If skiplines = 0
; Debug x.s
Else
; Debug y.s+" "+Trim(x.s)
While skiplines > 0
; Debug "; _"
skiplines = skiplines -1
Wend
y.s = ""
EndIf
ElseIf skiplines = 0
y.s = RTrim(Left(x.s,p))
skiplines = skiplines+1
Else
y.s = y.s+" "+Trim(Left(x.s,p))
skiplines = skiplines+1
EndIf
Next nn
Next nnn
t2 = GetTickCount_()-t1
Debug t2
End
i used this test file:
Code: Select all
Debug '_' + _
'_' ; this is not as easy
Debug '"' + _ ; as it looks
'a'
Debug "'" + "a" + "'" ; ah, the joys of coding
Debug Chr('_') _
; can make life so sweet
Debug "_ is the same as " _
+ Chr('_')
a.l = 1 + _ ; multiline ahoy
2 + _
'b' + _
Asc("c")
Procedure _test(a.l) ; hahaha, you thought this would be easy huh?
EndProcedure
Procedure test2(a.l) ; for example this '''""" _ _ may cause you problems _
EndProcedure
w_main_nr = OpenWindow(#PB_Any, _
10,10, _
100,100, _
#PB_Window_SystemMenu, _
"Test")
Repeat
event = WaitWindowEvent()
Until event = #PB_Event_CloseWindow
End
Posted: Sat Dec 10, 2005 4:38 pm
by Konne
Thats my little Code!
Code: Select all
Procedure MakeValidPB(LoadFile.s,SaveFile.s)
ReadFile(1,LoadFile.s)
*Buffer=AllocateMemory(Lof())
*Bufferoffset=*Buffer
Repeat
Wo+1
String.s=ReadString()
Len=Len(String.s)-1
Line=-1
DLine=-1
CommentAktiv=0
For i = 0 To Len
Select PeekS(@String.s+i,1)
Case Chr(34)
If Line=-1
DLine*-1
EndIf
Case "'"
If DLine=-1
Line*-1
EndIf
Case ";"
If DLine=-1 And Line=-1
NoComment.s=RTrim(PeekS(@String.s,i))
If PeekS(@NoComment.s+Len(NoComment.s)-1,1)="_"
String.s=NoComment.s
Else
CommentAktiv=1
EndIf
EndIf
EndSelect
Next
If BreakAktiv=1;PeekS(*Bufferoffset-3,1)="_" And NoBreak=0
String.s=Trim(String.s)
PokeS(*Bufferoffset-3,String.s+#CRLF$)
*Bufferoffset+Len(String.s)-1
Else
String.s=RTrim(String.s)
PokeS(*Bufferoffset,String.s+#CRLF$)
*Bufferoffset+Len(String.s)+2
EndIf
;Debug "Line "+Str(Wo)+" hat Commets "+Str(CommentAktiv)+" und Endet auf "+PeekS(*Bufferoffset-3,1)
If CommentAktiv=0 And PeekS(*Bufferoffset-3,1)="_"
BreakAktiv=1
Else
BreakAktiv=0
EndIf
Until Eof(1)<>0
CloseFile(1)
CreateFile(3,SaveFile.s)
WriteData(*Buffer,*Bufferoffset-*Buffer)
CloseFile(3)
FreeMemory(*Buffer)
EndProcedure
Start=ElapsedMilliseconds()
MakeValidPB("challenge.pb","Whatever.pb")
MessageRequester("Time","The transformation took "+Str(ElapsedMilliseconds()-Start)+" millisecondes.")
Posted: Sat Dec 10, 2005 9:51 pm
by blueznl
interesting, konne, we use a very different approach
i did a compare, and we do create binairy identical files (good stuff) but i'm afraid mine is a little faster

tried in running it 100 times on a file of 4000 lines with roughly a 100 'continuous' lines
result on my amd64: your code approx 17000 msec mine approx 14000 msec
i'm afraid, however, that both our routines may not be fast enough, as they would slow down the compilation process a little... it's noticable on my amd64, perhaps using a fastfile approach may help a little...
Code: Select all
Procedure MakeValidPB1(LoadFile.s,SaveFile.s)
ReadFile(1,LoadFile.s)
*Buffer=AllocateMemory(Lof())
*Bufferoffset=*Buffer
Repeat
Wo+1
String.s=ReadString()
Len=Len(String.s)-1
Line=-1
DLine=-1
CommentAktiv=0
For i = 0 To Len
Select PeekS(@String.s+i,1)
Case Chr(34)
If Line=-1
DLine*-1
EndIf
Case "'"
If DLine=-1
Line*-1
EndIf
Case ";"
If DLine=-1 And Line=-1
NoComment.s=RTrim(PeekS(@String.s,i))
If PeekS(@NoComment.s+Len(NoComment.s)-1,1)="_"
String.s=NoComment.s
Else
CommentAktiv=1
EndIf
EndIf
EndSelect
Next
If BreakAktiv=1;PeekS(*Bufferoffset-3,1)="_" And NoBreak=0
String.s=Trim(String.s)
PokeS(*Bufferoffset-3,String.s+#CRLF$)
*Bufferoffset+Len(String.s)-1
Else
String.s=RTrim(String.s)
PokeS(*Bufferoffset,String.s+#CRLF$)
*Bufferoffset+Len(String.s)+2
EndIf
;Debug "Line "+Str(Wo)+" hat Commets "+Str(CommentAktiv)+" und Endet auf "+PeekS(*Bufferoffset-3,1)
If CommentAktiv=0 And PeekS(*Bufferoffset-3,1)="_"
BreakAktiv=1
Else
BreakAktiv=0
EndIf
Until Eof(1)<>0
CloseFile(1)
CreateFile(3,SaveFile.s)
WriteData(*Buffer,*Bufferoffset-*Buffer)
CloseFile(3)
FreeMemory(*Buffer)
EndProcedure
Procedure makevalidpb2(loadfile.s,savefile.s)
ReadFile(1,loadfile.s)
CreateFile(2,savefile.s)
;
UseFile(1)
y.s = ""
While Eof(1) = 0
x.s = RTrim(ReadString())
;
x_p = @x
l = Len(x) ; length of string
t = 0 ; test flag, 1 is normal line, 2 is multiline
p = 0 ; counter / pointer
;
If l < 3 Or FindString(x.s," _",1) = 0 ; findstring is faster than my code
t = 1
Else
b = PeekB(x_p+p)
Repeat
If b = 34 ; double quote char
Repeat ; repeat until another one is hit
p = p+1
b = PeekB(x_p+p)
Until b = 34 Or p = l-2
p = p+1
b = PeekB(x_p+p)
ElseIf b = 39 ; single quote char
Repeat ; repeat until another one is hit
p = p+1
b = PeekB(x_p+p)
Until b = 39 Or p = l-2
p = p+1
b = PeekB(x_p+p)
ElseIf b = ' ' ; ah a space
Repeat
p = p+1
b = PeekB(x_p+p)
Until b <> ' ' Or p = l-1 ; keep on going until it's no longer a space
If b ='_'
If p = l-1
t = 2 ; we're on an eol with a [space][underscore]
p = p-1 ; adjust pointer to spot before [underscore]
Else
p = p+1
b = PeekB(x_p+p)
If b = ' '
t = 3 ; we just passed a [space][underscore][space]
p = p-2 ; adjust pointer to spot before [underscore]
EndIf
EndIf
EndIf
ElseIf b = ';' Or b = ':'
t = 1
ElseIf p < l-1
p = p+1
b = PeekB(x_p+p)
Else
t = 1
EndIf
Until t <> 0
EndIf
;
If t=1
If skiplines = 0
UseFile(2)
WriteString(x.s+#CRLF$)
UseFile(1)
Else
y.s = y.s+" "+Trim(x.s)+#CRLF$
While skiplines > 0
; y.s = y.s+"; _"+#CRLF$
skiplines = skiplines -1
Wend
UseFile(2)
WriteString(y.s)
UseFile(1)
y.s = ""
EndIf
ElseIf skiplines = 0
y.s = RTrim(Left(x.s,p))
skiplines = skiplines+1
Else
y.s = y.s+" "+Trim(Left(x.s,p))
skiplines = skiplines+1
EndIf
Wend
If y.s <> ""
UseFile(2)
WriteString(y.s+#CRLF$)
EndIf
CloseFile(1)
CloseFile(2)
EndProcedure
Start=ElapsedMilliseconds()
For n = 1 To 100
MakeValidPB2("challenge2.pb","answer2.pb")
Next n
MessageRequester("Time","Blueznl's transformation took "+Str(ElapsedMilliseconds()-Start)+" millisecondes.")
Start=ElapsedMilliseconds()
For n = 1 To 100
MakeValidPB1("challenge2.pb","answer1.pb")
Next n
MessageRequester("Time","The transformation took "+Str(ElapsedMilliseconds()-Start)+" millisecondes.")
Posted: Sat Dec 10, 2005 10:33 pm
by Konne
You're right, yours is a little faster.
But I think that this doesn't matter because the main Fact why the Procedures are "Slow" is because of the File commands, which wouldn't be nessecary if the file would be parsed by the Compiler.
Offcause I tryed to make my Code faster, and replaced the String Ifs with Byte Ifs (like in your Code), but it wasn't much faster that before

Posted: Sun Dec 11, 2005 12:02 am
by blueznl
well i put it into codecaddy and it works, speed was not as bad as i feared, though that is on a FAST machine, amd64-3000, haven't checked it yet on slower machines...
Posted: Sun Dec 11, 2005 1:27 am
by PB
> this one does comments, and makes no mistakes on _ in strings
Strings don't need to be parsed anyway. The whole concept is that any line
ENDING WITH space+underscore gets continued, so all you need to do is
check Right(line$,2) and go from there. All this char-by-char parsing is just
doing obsolete work.
