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

Code: Select all

Debug 2+2+2

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! :oops: :oops: :oops:

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

Code: Select all

a.s = "dit is een _
test"
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. :wink:
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 :D

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. :lol: