Page 1 of 2

PB code assistant compiler tool

Posted: Mon Jun 09, 2025 12:49 am
by idle
A proof of concept using a compiler driver and mistral.ai codestral to correct syntax errors and generate proceedures

A PB code assistant could facilitate
Syntax corrections [x]
Documentation
Debugging
Fill in the middle [x]
Code generation

Added FIM (fill in the middle)
you need to add the FIM macro to the top of your code for the instructions

Code: Select all

Macro FIM(query) 
  ;FIM query 
EndMacro    
And an example, trying to get inlinec and asm working with an odd instruction

Code: Select all

EnableExplicit 
Macro FIM(query) 
  ;FIM query 
EndMacro   

Procedure foo() 
  gg.s = "hello foo"
  PrintN(gg) 
EndProcedure   

Structure m128
  StructureUnion
    a.a[16]
    u.u[8] 
    l.l[4]
    q.q[2]
  EndStructureUnion
EndStructure   

FIM("please write bswap128 using the m128 Structure with both inline c using __builtin_bswap64 And inline Asm For x86 And x64" + 
    "using appropriate CompilerIf CompilerElse statements For c backend and for x86 and x64 asm versions eg #PB_Compiler_Backend = #PB_Backend_C, " +
    "and #PB_Compiler_Processor=#PB_Processor_x64. use intel format asm with ! to denote inline asm, use movbe instruction for swap" +
    "variables in inline c are prefixed with v_ like !v_h = __builtin_bswap64(v_h); asm variables preceeded by p.v_ eg !mov [p.v_l],rax")
Procedure bswap128(*m.m128)  
    
  Protected h.q = *m\q[1] 
  Protected l.q = *m\q[0]  
  
  
  !movbe edx,[p.v_h]
  !movbe eax,[p.v_h+ 4]
  !mov [p.v_h], dword eax 
  !mov [p.v_h+4], dword edx 
  !movbe edx,[p.v_l]
  !movbe eax,[p.v_l + 4]
  !mov [p.v_l], dword eax 
  !mov [p.v_l+4], dword edx 
  
  *m\q[1] = l 
  *m\q[0] = h 
    
EndProcedure 


gg.s = "hello World"
x.i = @gg & $ffff 

OpenConsole("test")
PrintN(gg.s)
foo()
Inpu()
CloseConsole(


This is set up as a compiler tool but if you want to run in debug mode comment out line 314
file$ = ProgramParameter() and set it to a source file that's got errors in it


To use as a compiler tool set the Arguments to %FILE and then set a shortcut like ALT + F5

When it runs it will compile the code with a syntax check pass the error onto the ai along with the code then the compiler will check it again and if successful compile it with debugger and run it, and output the corrected code to checkcode.pb and open it in the IDE on windows.

Code: Select all

EnableExplicit

;if you only want to test use this 
#Userkey$ = "idletestkey" 
#CHATURL$ = "https://atomicwebserver.com/v1/chat/pbcode"

;if you want to try more get your own key
;#Userkey$ = "your mistral api key" 
;#CHATURL$ = "https://api.mistral.ai/v1/chat/completions" 

Global compiler,inputcode$,outputcode$,chaterror$ 

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  #Compiler = #PB_Compiler_Home + "compilers\pbcompiler.exe"
CompilerElse
  #Compiler = #PB_Compiler_Home + "compilers/pbcompiler"
CompilerEndIf

Procedure StartCompiler()
  Protected cmd$, result
  Protected temp$ = GetTemporaryDirectory()
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    cmd$ = "/STANDBY"
  CompilerElse
    cmd$ = "-sb"
  CompilerEndIf
  
  result = RunProgram(#Compiler, cmd$, temp$, #PB_Program_Open | #PB_Program_Read | #PB_Program_Write | #PB_Program_Hide)
  ProcedureReturn result
EndProcedure

Procedure StopCompiler(compiler)
  Protected cmd$
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    cmd$ = "END"
  CompilerElse
    cmd$ = "END"
  CompilerEndIf
  
  WriteProgramStringN(compiler, cmd$)
  WaitProgram(compiler, 5000)
  CloseProgram(compiler)
EndProcedure

Procedure SendCompilerCommand(compiler, command$)
  If ProgramRunning(compiler)
    WriteProgramStringN(compiler, command$)
  EndIf
EndProcedure

Procedure.s GetCompilerOutput(compiler)
  If AvailableProgramOutput(compiler)
    ProcedureReturn ReadProgramString(compiler)
  EndIf
EndProcedure

Procedure WaitCompilerReady(compiler)
  Protected out$
  
  While out$ <> "READY" And Left(out$, 5) <> "ERROR"
    out$ = GetCompilerOutput(compiler)
  Wend
EndProcedure

Procedure.s CheckSyntax(compiler, code$, bProgress = 0)
  Protected temp$ = GetTemporaryDirectory()
  Protected source$ = temp$ + "checkcode"
  Protected out$, error$
  Protected fn = CreateFile(-1, source$ + ".pb")
  
  If fn
    WriteString(fn, code$, #PB_UTF8)
    CloseFile(fn)
    SendCompilerCommand(Compiler, "SOURCE" + Chr(9) + source$ + ".pb")
    SendCompilerCommand(Compiler, "TARGET" + Chr(9) + source$ + ".exe")
    SendCompilerCommand(Compiler, "COMPILE" + Chr(9) + "PROGRESS" + Chr(9) + "SYNTAX")
    
    While out$ <> "SUCCESS" And Left(out$, 5) <> "ERROR"
      out$ = GetCompilerOutput(compiler)
      
      If out$ <> ""
        If bProgress
          PrintN(out$)
        EndIf
      EndIf
    Wend
    
    If Left(out$, 5) = "ERROR"
      While out$ <> "OUTPUT" + #TAB$ + "COMPLETE"
        out$ = GetCompilerOutput(compiler)
        
        If (out$ <> "" And Left(out$, 6) <> "OUTPUT")
          error$ + out$
          
          If bProgress
            PrintN(out$)
          EndIf
        EndIf
      Wend
      out$ = error$
    EndIf
    ReplaceString(out$,Chr(9),Chr(32),#PB_String_InPlace)  
    
    ProcedureReturn out$
  EndIf
EndProcedure

Procedure CompileWithDebug(compiler, code$, bProgress = 0)
  Protected out$,prog,Output$
  Protected temp$ = GetTemporaryDirectory()
  Protected source$ = temp$ + "checkcode"
  Protected fn = CreateFile(-1, source$ + ".pb")
  
  If fn
    If Not FindString(code$,"EnableExplicit") 
      WriteStringN(fn,"EnableExplicit") 
    EndIf   
    WriteStringN(fn,"Calldebugger")
    WriteString(fn, code$, #PB_UTF8)
    CloseFile(fn)
    SendCompilerCommand(Compiler, "SOURCE" + Chr(9) + source$ + ".pb")
    SendCompilerCommand(Compiler, "TARGET" + Chr(9) + source$ + ".exe")
    SendCompilerCommand(Compiler, "COMPILE" + Chr(9) + "PROGRESS" + Chr(9) + "DEBUGGER" + Chr(9) +"PURIFIER")
    
    While out$ <> "SUCCESS" And Left(out$, 5) <> "ERROR"
      out$ = GetCompilerOutput(compiler)
      
      If out$ <> ""
        If bProgress
          PrintN(out$)
        EndIf
      EndIf
    Wend
    
    If out$ = "SUCCESS"
      RunProgram(#PB_Compiler_Home+"compilers/PBDebugger.exe", source$+".exe","")
      RunProgram(source$ + ".pb")
    EndIf
    
  EndIf
EndProcedure

Procedure.s RequestInstruct(instruction$,request$) 
  Protected input$,out$,status$,HttpRequest 
  Protected NewMap header.s() 
  
  request$ = EscapeString(request$,#PB_String_EscapeJSON)   
  
  header("Content-Type") = "application/json" 
  header("Accept") = "application/json" 
  header("Expect") = ""
  header("Authorization") = "Bearer " + #Userkey$
  
  input$ = "{ " +
          ~"\"model\": \"codestral-latest\"," +     
          ~"\"temperature\": 0.1," +
          ~"\"top_p\": 1," + 
          ~"\"max_tokens\": 65536," +
          ~"\"messages\": [{\"role\": \"user\", \"content\":\"" + instruction$ + " " + request$ +
          ~"\"" + "}]}" 
  
  HttpRequest = HTTPRequest(#PB_HTTP_Post,#CHATURL$,Input$,0,header())  
  If HttpRequest
    Status$ = HTTPInfo(HTTPRequest, #PB_HTTP_StatusCode)
    If status$ = "200" 
      out$ = HTTPInfo(HTTPRequest, #PB_HTTP_Response)
      PrintN(out$)
    Else 
      PrintN("ERROR: " + HTTPInfo(HTTPRequest, #PB_HTTP_ErrorMessage) + " [" + HTTPInfo(HTTPRequest, #PB_HTTP_StatusCode) + "]")
      chaterror$ = status$ + " " + HTTPInfo(HTTPRequest, #PB_HTTP_Response)
    EndIf   
    FinishHTTP(HTTPRequest)
  Else
    chaterror$ = "failed to connect" 
  EndIf
  
  ProcedureReturn out$ 
  
EndProcedure  

Procedure.s ExtractContent(JSON_Response$) 
  Protected pos,epos,code$ 
  pos = FindString(JSON_Response$,"content")
  If pos 
    pos + 10
    epos = FindString(JSON_Response$,"}",pos+1) -1 
    
    code$ = Mid(JSON_Response$,pos,epos-pos) 
    code$ = UnescapeString(code$,#PB_String_EscapeJSON) 
    
    Debug code$
  EndIf 
  code$ = RemoveString(code$,"```")
  ProcedureReturn code$ 
  
EndProcedure   

Procedure.s CorrectSyntax(compiler,code$) 
  
  Protected UserInstruction$,JSON_Response$,return_message$
  Protected pass 
  
  return_message$ = CheckSyntax(compiler, code$)
  
  PrintN(return_message$ )
  
  If Left(return_message$,7) = "MESSAGE"
       
    While return_message$ <> "SUCCESS"
      If pass > 4 
        MessageRequester("code check", code$, #PB_MessageRequester_Info)
        Break
      Else
        pass + 1
      EndIf
      UserInstruction$ = "Fix the following code for the PureBasic language. use purebasic syntax only do not define variables with 'as' use purebasic types or structures if given." +
                         "Only return the complete repaired code, with no extra comments, do not move Align keyword if found to a new line. If you Encounter EnableExplicit " + 
                         "try To Define the variables within the scope using either Global Or Protected " + 
                         "if a variable is only found in a Procedure and not within the global scope use Protected, make sure to use the variable type." +
                         "Keep the original formatting: do not add the word purebasic to the code, do not process lines with comments denoted by ; at the start but add them back in. Here is the error message followed by the code: " + return_message$
      
      JSON_Response$ = RequestInstruct(UserInstruction$,code$)
      PrintN(JSON_Response$  )
      If JSON_Response$ <> ""  
        code$ = ExtractContent(JSON_Response$)
        PrintN(code$)
        return_message$ = CheckSyntax(compiler, code$)
      Else 
        MessageRequester("code check", chaterror$, #PB_MessageRequester_Info)
        Break
      EndIf   
    Wend 
    
    PrintN(return_message$ )
    
    If return_message$ = "SUCCESS" 
      ProcedureReturn code$ 
    Else 
      ProcedureReturn "" 
    EndIf 
    
  Else 
    ProcedureReturn code$
  EndIf 
  
EndProcedure   

Procedure.s FIM(compiler,List lcode.s()) 
  
  Protected code$ 
  Protected fim$,instruction$,inst$,JSON_Response$  
  Protected pos,state  
  
  ForEach lcode.s() 
    Debug lcode() 
    pos = FindString(lcode(),"FIM") 
    If pos > 0
      If FindString(lcode(),"FIM(query)") = 0 And FindString(lcode(),";FIM query") = 0 
        state = 1 
        instruction$ + lcode() 
      EndIf   
    EndIf   
    If state > 0 
      pos = FindString(lcode(),"Procedure") 
      If pos > 0
        If Not FindString(lcode(),"EndProcedure") 
          state = 2 
        EndIf   
      EndIf    
      pos = FindString(lcode(),"EndProcedure") 
      If pos > 0
        state=3 
      EndIf 
    EndIf 
    
    If state = 0 
      code$ + lcode() + #LF$
    ElseIf state = 1 
      code$ + ";" + lcode() + #LF$
      instruction$ + lcode() + #LF$
    ElseIf state = 2  
     
      fim$ + lcode() + #LF$
    ElseIf state = 3 
       fim$ + lcode() + #LF$
       
      inst$ = EscapeString("do not add any comentary just fill in the PureBasic code with the following instruction between the named Procedure and Endprocedue at the end of the code here is the instruction " + instruction$ + " here is the body of code for reference ",#PB_String_EscapeJSON) 
       
      JSON_Response$  = RequestInstruct(inst$,code$+fim$)      
      If JSON_Response$ <> ""  
        code$ = ExtractContent(JSON_Response$)
        PrintN(code$)
      EndIf   
          
      state = 0     
    EndIf   
    
  Next   
  
  ProcedureReturn code$ 
    
EndProcedure   

Global code$ ,File$,fn
Define NewList lcode.s()

OpenConsole()

compiler = StartCompiler()

If compiler
  
  file$ = ProgramParameter()
  ;file$ = "C:\Users\idle\AppData\Local\Temp\check.pb"
  PrintN(file$) 
  
  If FileSize(file$) >= 0
       
    fn = ReadFile(-1, file$)
    If fn 
      While Not Eof(fn)  
        AddElement(lcode())
        lcode() = ReadString(fn,#PB_UTF8) 
      Wend   
    EndIf
    
    code$ = FIM(compiler,lcode())
    
    PrintN(code$)     
       
    outputcode$ = CorrectSyntax(compiler,code$)
    PrintN(outputcode$) 
    
    If outputcode$ <> "" 
      CompileWithDebug(compiler, outputcode$, 1)
      StopCompiler(compiler)
    EndIf
  Else 
    MessageRequester("code check", "couldn't start compiler", #PB_MessageRequester_Info)
    End 
  EndIf   
  
EndIf
Input() 
CloseConsole()


Re: PB code assistant compiler tool

Posted: Mon Jun 09, 2025 8:43 am
by Caronte3D
Wow! Nice!
I like it, the response is faster enough, time to experiment 8)
Thanks! :wink:

Re: PB code assistant compiler tool

Posted: Mon Jun 09, 2025 11:11 am
by idle
Caronte3D wrote: Mon Jun 09, 2025 8:43 am Wow! Nice!
I like it, the response is faster enough, time to experiment 8)
Thanks! :wink:
Yes but it's still buggy.

Pbcompiler only returns the 1st syntax error. The llm will try to correct the lot but it doesn't know how to declare variables if they're not declared.

It certainly has potential and for me being a severe dyslexic, I usually hit F5 half a dozen times before a successful compile.

Re: PB code assistant compiler tool

Posted: Mon Jun 09, 2025 11:21 am
by Caronte3D
I don't care to much about syntax correction, but instead, I like the idea of select a code part (i.e. a procedure) and let the AI to solve some incorrect behaviour in her logic :wink:
Anyway the AI is a must and PB should take this train from the beginning.

Re: PB code assistant compiler tool

Posted: Mon Jun 09, 2025 12:01 pm
by idle
Yes I think it would be a good thing to look into and its also something Fred could potentialy benefit from financially while providing a benefit to the community.

Re: PB code assistant compiler tool

Posted: Tue Jun 10, 2025 8:31 am
by dige
@Idle: Thank you for sharing this with us. This is a great idea. This could become the “KillerApp” tool for the IDE :D
I hadn't tested Mistral before. The first impression is already impressive.

Re: PB code assistant compiler tool

Posted: Tue Jun 10, 2025 10:31 am
by Fred
I'm curious to see how it can be properly integrated in the IDE

Re: PB code assistant compiler tool

Posted: Wed Jun 11, 2025 1:57 am
by idle
@Dige
Yes it would be quite cool to have, it's code generation isn't exactly stella compared to Claude which can even tackle inline c and asm but I think Codestral has potential but it really needs more training on PB, which could be done via the documentation (it doesn't appear to be trained on the pb documentation) but there are a still a lot of hoops to jump through to build a fine tuned model and it also has a cost, though it's only 2$ a month to store the fine tuned model but then I don't know if that model can be shared or who retains the rights to it...

@Fred
No idea yet but the logical path is to develop it as a series of IDE tools to take advantage of the Api while its free to use and get it working through the compiler chain first.

Syntax checking is should be easy enough but I don't know if it retains the code from the previous pass yet so you wouldn't have to feed it the whole code when the compiler halts on the next error it encounters, if it retains the context then you can just pass the compiler message and insert the result into the source. Though when you ask it to correct the syntax it does the whole document in one go but it needs better prompting to declare variables if EnableExplicit is used and will probably take some time to do. It was returning the checked code in python comments for crying out loud! :x

Debugging I haven't actually considered yet, though I'm thinking it can be done in much the same way, though it probably won't be much help if a source is threaded. it's more to validate the code.

Fill in the middle and code generation, could be passed on as a selection using a comment that serves as both the prompt and documentation of the function.

Generation well just pop up a prompt box ask it to do the code insert at current location and run it through the chain to correct it rinse and repeat.

I don't really know if mistral.ai platform is the ideal solution, but I think it could be a good fit for Fred if he wanted to bolt Ai into Purebasic and he could retain some control of the product and also generate an additional revenue stream which would otherwise go directly to "all your data belongs to us mega corp" I see that as a Win/Win. if he gets to clip the ticket.

It's defiantly possible to make a fine tuned model iteratively based on Codestral and while it appears you can feed it github repos I don't think that means pb source code, it really needs a specific format with prompts and explanations of the code in jsonl format. And it's costs $4 per job and $2 a month for storage. The PB documentation would of course be a good place to start and then overtime code can be added with the explanations and prompts used to generate the code. Embeddings can be plain code but then that's just an archive it can query with NLP and fetch from though that would be useful, there's a lot of code on the French and German forums that's not here and if it was all available to us more the better but obviously you want the code to be good which is the whole point of validating the code via compiling it rather than scraping the forums.

Re: PB code assistant compiler tool

Posted: Wed Jun 11, 2025 4:09 am
by idle
updated 1st post to try and get it to define variables and also running in standalone debugger.
injecting EnableExplicit if not found and CallDebugger at the top of the code so it can be stepped through

Re: PB code assistant compiler tool

Posted: Fri Jun 13, 2025 4:05 am
by idle
updates 1st post so you can use it directly for now via atomicwebserver
or direct to mistral with your own key.

Re: PB code assistant compiler tool

Posted: Sat Jun 14, 2025 12:09 am
by idle
fixed bug with http request to server

Re: PB code assistant compiler tool

Posted: Sun Jun 15, 2025 3:31 am
by idle
added fill in the middle using a macro FIM so it's easier to read the instruction

use it like this

Code: Select all

EnableExplicit 
Macro FIM(query) 
  ;FIM query 
EndMacro   

Structure m128
  StructureUnion
    a.a[16]
    u.u[8] 
    l.l[4]
    q.q[2]
  EndStructureUnion
EndStructure   

FIM("please write bswap128 using the m128 Structure with both inline c using __builtin_bswap64 And inline Asm For x86 And x64" + 
    "using appropriate CompilerIf CompilerElse statements For c backend and for x86 and x64 asm versions eg #PB_Compiler_Backend = #PB_Backend_C, " +
    "and #PB_Compiler_Processor=#PB_Processor_x64. use intel format asm with ! to denote inline asm, use movbe instruction for swap" +
    "variables in inline c are prefixed with v_ like !v_h = __builtin_bswap64(v_h); asm variables preceeded by p.v_ eg !mov [p.v_l],rax")
Procedure bswap128(*m.m128)  
    
  Protected h.q = *m\q[1] 
  Protected l.q = *m\q[0]  
    
  *m\q[1] = l 
  *m\q[0] = h 
    
EndProcedure 
don't expect stella results.

Re: PB code assistant compiler tool

Posted: Sun Jun 15, 2025 3:52 pm
by JHPJHP
Hi idle,

This is fantastic.

So many of the computer and phone apps I use now have an AI option, which is quickly becoming invaluable, why should PureBasic be any different? If anything, AI code assistance is already a big part of the culture, bringing it directly to the IDE seems like an obvious next step.

I hope this continues to move forward with growing support from the community; remember, everything great started a little buggy.

Re: PB code assistant compiler tool

Posted: Sun Jun 15, 2025 6:30 pm
by Caronte3D
+1

Re: PB code assistant compiler tool

Posted: Sun Jun 15, 2025 9:23 pm
by idle
JHPJHP wrote: Sun Jun 15, 2025 3:52 pm Hi idle,

This is fantastic.

So many of the computer and phone apps I use now have an AI option, which is quickly becoming invaluable, why should PureBasic be any different? If anything, AI code assistance is already a big part of the culture, bringing it directly to the IDE seems like an obvious next step.

I hope this continues to move forward with growing support from the community; remember, everything great started a little buggy.
Thanks and yes I think we need it built into the ide but it will take some time to work out.
I still have to check it with multiple FIM()
The question is does the AI retain a context window that looks up the context from your api key or would you have to keep the connection open instead of closing it after every request and if that's the case can you ignore FinishHTTP(HTTPRequest) and reuse it?

It seems a bit silly sending all the code to a point to give it context of a function which uses a structure at the top of your code but if it retained that it would really help reduce the processing.