Making simple apps with AI

Everything else that doesn't fall into one of the other PB categories.
BarryG
Addict
Addict
Posts: 4292
Joined: Thu Apr 18, 2019 8:17 am

Making simple apps with AI

Post by BarryG »

I wanted to check how the current state of AI was with creating a PureBasic app with AI, so I asked ChatGPT: "make a calc app for windows with buttons using purebasic". Below is a screenshot (and code) of the answer it gave. I told it that #PB_String_Right is invalid, so it removed it. I could then literally run what it gave. Amazing from just a simple prompt! :shock: This sort of compilable answer was unheard of not so long ago.

This literally took less than one minute to create. If I were coding it from scratch, I'd be looking at around 30 minutes at least if coding the UI by hand.

Image

Code: Select all

If OpenWindow(0, 0, 0, 240, 300, "Calculator", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

  StringGadget(0, 10, 10, 220, 30, "", #PB_String_ReadOnly)

  ButtonGadget(1,  10,  50, 50, 40, "7")
  ButtonGadget(2,  70,  50, 50, 40, "8")
  ButtonGadget(3, 130,  50, 50, 40, "9")
  ButtonGadget(4, 190,  50, 40, 40, "/")

  ButtonGadget(5,  10, 100, 50, 40, "4")
  ButtonGadget(6,  70, 100, 50, 40, "5")
  ButtonGadget(7, 130, 100, 50, 40, "6")
  ButtonGadget(8, 190, 100, 40, 40, "*")

  ButtonGadget(9,  10, 150, 50, 40, "1")
  ButtonGadget(10, 70, 150, 50, 40, "2")
  ButtonGadget(11,130, 150, 50, 40, "3")
  ButtonGadget(12,190, 150, 40, 40, "-")

  ButtonGadget(13,10, 200, 50, 40, "0")
  ButtonGadget(14,70, 200, 50, 40, "C")
  ButtonGadget(15,130,200, 50, 40, "=")
  ButtonGadget(16,190,200, 40, 40, "+")

  current$ = ""
  lastValue.d = 0
  operation$ = ""
  newEntry = #True

  Repeat
    event = WaitWindowEvent()

    If event = #PB_Event_Gadget
      g = EventGadget()

      Select g
        Case 1 To 3, 5 To 7, 9 To 11, 13
          If newEntry
            current$ = ""
            newEntry = #False
          EndIf
          current$ + GetGadgetText(g)
          SetGadgetText(0, current$)

        Case 4, 8, 12, 16
          lastValue = ValD(current$)
          operation$ = GetGadgetText(g)
          newEntry = #True

        Case 14
          current$ = ""
          lastValue = 0
          operation$ = ""
          newEntry = #True
          SetGadgetText(0, "")

        Case 15
          value.d = ValD(current$)

          Select operation$
            Case "+"
              value = lastValue + value
            Case "-"
              value = lastValue - value
            Case "*"
              value = lastValue * value
            Case "/"
              If value <> 0
                value = lastValue / value
              Else
                value = 0
              EndIf
          EndSelect

          current$ = StrD(value)
          SetGadgetText(0, current$)
          newEntry = #True
      EndSelect
    EndIf

  Until event = #PB_Event_CloseWindow

EndIf

Image
BarryG
Addict
Addict
Posts: 4292
Joined: Thu Apr 18, 2019 8:17 am

Re: Making simple apps with AI

Post by BarryG »

I then introduced a bug by changing line 40 from "newEntry = #False" to "newEntry = #True". I asked ChatGPT to find the bug. The answer:

Image

Amazing! :shock:
threedslider
Enthusiast
Enthusiast
Posts: 494
Joined: Sat Feb 12, 2022 7:15 pm

Re: Making simple apps with AI

Post by threedslider »

AI not so smarter ... :mrgreen:
BarryG
Addict
Addict
Posts: 4292
Joined: Thu Apr 18, 2019 8:17 am

Re: Making simple apps with AI

Post by BarryG »

Why? It found the bug.
threedslider
Enthusiast
Enthusiast
Posts: 494
Joined: Sat Feb 12, 2022 7:15 pm

Re: Making simple apps with AI

Post by threedslider »

Yes and sometime AI gives some wrong from syntax ... :shock:
User avatar
mk-soft
Always Here
Always Here
Posts: 6471
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Making simple apps with AI

Post by mk-soft »

Ms Copilot got it done right away ;)
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
minimy
Enthusiast
Enthusiast
Posts: 798
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: Making simple apps with AI

Post by minimy »

Did he seriously do that?.
I have to invest a little time with AIs, they can be a powerful ally.
Thanks for share BarryG, very interesting.
If translation=Error: reply="Sorry, Im Spanish": Endif
User avatar
skywalk
Addict
Addict
Posts: 4276
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Making simple apps with AI

Post by skywalk »

AI is a game changer as long as your data is private.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
BarryG
Addict
Addict
Posts: 4292
Joined: Thu Apr 18, 2019 8:17 am

Re: Making simple apps with AI

Post by BarryG »

minimy wrote: Mon Dec 29, 2025 2:28 amDid he seriously do that?
Yes. That's why I took a screenshot of its answer. All I did was correct it about #PB_String_Right.
miso
Enthusiast
Enthusiast
Posts: 622
Joined: Sat Oct 21, 2023 4:06 pm
Location: Hungary

Re: Making simple apps with AI

Post by miso »

I'm still not convinced, and I accept that this might come from my conservativism...
While not denying it is gamechanger, the code generation is something like the image generation for me. It will draw unnatural number of fingers, and you won't notice it. A better trained AI will generate less errors (will be more precise) but will be less creative (will deviate less from the input material it has been fed with the learning process)

Concerning codewriting I see the future of AI in live code examination, when the AI can warn the programmer he is making a mistake. Also, it can give you advice in topics you are less familiar with great results. So I'm not saying AI is useless, just that it is useful in a different way.
User avatar
minimy
Enthusiast
Enthusiast
Posts: 798
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: Making simple apps with AI

Post by minimy »

miso wrote: Mon Dec 29, 2025 9:36 am I'm still not convinced, and I accept that this might come from my conservativism...
While not denying it is gamechanger, the code generation is something like the image generation for me. It will draw unnatural number of fingers, and you won't notice it. A better trained AI will generate less errors (will be more precise) but will be less creative (will deviate less from the input material it has been fed with the learning process)

Concerning codewriting I see the future of AI in live code examination, when the AI can warn the programmer he is making a mistake. Also, it can give you advice in topics you are less familiar with great results. So I'm not saying AI is useless, just that it is useful in a different way.
exactly! That's what I meant. Sometimes I make mistakes, because sometimes when writing quickly I put an extra letter in some variable and the debugger does not find the error. This can be a quick way to spot those and other errors.
If translation=Error: reply="Sorry, Im Spanish": Endif
BarryG
Addict
Addict
Posts: 4292
Joined: Thu Apr 18, 2019 8:17 am

Re: Making simple apps with AI

Post by BarryG »

minimy wrote: Mon Dec 29, 2025 12:58 pmsometimes when writing quickly I put an extra letter in some variable and the debugger does not find the error
Some people here will tell you to use EnableExplicit to stop that, but I hate manually defining every variable when coding. :P

Also, EnableExplicit is useless for finding bugs caused by valid typos, like this:

Code: Select all

EnableExplicit

Define but1,but2,ev

OpenWindow(0,200,200,100,100,"test",#PB_Window_SystemMenu)
but1=ButtonGadget(#PB_Any,10,10,80,20,"Button 1")
but1=ButtonGadget(#PB_Any,10,40,80,20,"Button 2") ; <- BUG! But EnableExplicit doesn't know.

Repeat
  ev=WaitWindowEvent()
  If ev=#PB_Event_Gadget
    Select EventGadget()
      Case but1 : Debug "Button 1 clicked"
      Case but2 : Debug "Button 2 clicked"
    EndSelect
  EndIf
Until ev=#PB_Event_CloseWindow
In this code, EnableExplicit does not warn about the bug of a mistyped variable name (line 7).

But when I ask ChatGPT what's wrong, it spots the error and tells me about it and how to fix it (screenshot cropped):

Image

Again, it's pretty amazing that AI can spot what I intended and detect the bug for it. :shock:
User avatar
minimy
Enthusiast
Enthusiast
Posts: 798
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Re: Making simple apps with AI

Post by minimy »

Yes, is amazing. Im testing copilot and is really nice when need small piece of code like math formulas.
In my few tests, some times i need change the types like float for .f or any small things.
Sometimes it seems like he mixes C,C++ with purebasic and makes a coconut smoothie hahaha
If translation=Error: reply="Sorry, Im Spanish": Endif
User avatar
idle
Always Here
Always Here
Posts: 6130
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Making simple apps with AI

Post by idle »

asked claude
"write a calculator app in purebasic using shunting yard and rpn eval function"

Code: Select all

; Calculator using Shunting Yard Algorithm and RPN Evaluation
; Supports +, -, *, /, ^, and parentheses

Structure Token
  value.s
  type.i  ; 0=number, 1=operator, 2=left paren, 3=right paren
EndStructure

Procedure.i GetPrecedence(op.s)
  Select op
    Case "+", "-"
      ProcedureReturn 1
    Case "*", "/"
      ProcedureReturn 2
    Case "^"
      ProcedureReturn 3
  EndSelect
  ProcedureReturn 0
EndProcedure

Procedure.i IsLeftAssociative(op.s)
  If op = "^"
    ProcedureReturn #False
  EndIf
  ProcedureReturn #True
EndProcedure

Procedure.s ShuntingYard(expression.s)
  NewList output.s()
  NewList opStack.s()
  Protected i, len, char.s, token.s, isNumber
  
  len = Len(expression)
  i = 1
  
  While i <= len
    char = Mid(expression, i, 1)
    
    ; Skip whitespace
    If char = " "
      i + 1
      Continue
    EndIf
    
    ; Check if it's a number or decimal point
    If (char >= "0" And char <= "9") Or char = "."
      token = ""
      While i <= len
        char = Mid(expression, i, 1)
        If (char >= "0" And char <= "9") Or char = "."
          token + char
          i + 1
        Else
          Break
        EndIf
      Wend
      AddElement(output())
      output() = token
      Continue
    EndIf
    
    ; Check for operators
    If FindString("+-*/^", char, 1)
      While ListSize(opStack()) > 0
        LastElement(opStack())
        If opStack() <> "(" And 
           ((IsLeftAssociative(char) And GetPrecedence(char) <= GetPrecedence(opStack())) Or
            (Not IsLeftAssociative(char) And GetPrecedence(char) < GetPrecedence(opStack())))
          AddElement(output())
          output() = opStack()
          DeleteElement(opStack())
        Else
          Break
        EndIf
      Wend
      AddElement(opStack())
      opStack() = char
    EndIf
    
    ; Left parenthesis
    If char = "("
      AddElement(opStack())
      opStack() = char
    EndIf
    
    ; Right parenthesis
    If char = ")"
      While ListSize(opStack()) > 0
        LastElement(opStack())
        If opStack() <> "("
          AddElement(output())
          output() = opStack()
          DeleteElement(opStack())
        Else
          DeleteElement(opStack())
          Break
        EndIf
      Wend
    EndIf
    
    i + 1
  Wend
  
  ; Pop remaining operators
  While ListSize(opStack()) > 0
    LastElement(opStack())
    AddElement(output())
    output() = opStack()
    DeleteElement(opStack())
  Wend
  
  ; Build result string
  Protected result.s = ""
  ForEach output()
    If result <> ""
      result + " "
    EndIf
    result + output()
  Next
  
  ProcedureReturn result
EndProcedure

Procedure.d EvaluateRPN(rpn.s)
  NewList stack.d()
  Protected token.s, i, len, pos, a.d, b.d, result.d
  
  len = Len(rpn)
  i = 1
  token = ""
  
  While i <= len
    Protected char.s = Mid(rpn, i, 1)
    
    If char = " "
      If token <> ""
        If FindString("+-*/^", token, 1) And Len(token) = 1
          ; It's an operator
          If ListSize(stack()) >= 2
            LastElement(stack())
            b = stack()
            DeleteElement(stack())
            LastElement(stack())
            a = stack()
            DeleteElement(stack())
            
            Select token
              Case "+"
                result = a + b
              Case "-"
                result = a - b
              Case "*"
                result = a * b
              Case "/"
                If b <> 0
                  result = a / b
                Else
                  result = 0
                EndIf
              Case "^"
                result = Pow(a, b)
            EndSelect
            
            AddElement(stack())
            stack() = result
          EndIf
        Else
          ; It's a number
          AddElement(stack())
          stack() = ValD(token)
        EndIf
        token = ""
      EndIf
    Else
      token + char
    EndIf
    
    i + 1
  Wend
  
  ; Handle last token
  If token <> ""
    If FindString("+-*/^", token, 1) And Len(token) = 1
      If ListSize(stack()) >= 2
        LastElement(stack())
        b = stack()
        DeleteElement(stack())
        LastElement(stack())
        a = stack()
        DeleteElement(stack())
        
        Select token
          Case "+"
            result = a + b
          Case "-"
            result = a - b
          Case "*"
            result = a * b
          Case "/"
            If b <> 0
              result = a / b
            Else
              result = 0
            EndIf
          Case "^"
            result = Pow(a, b)
        EndSelect
        
        AddElement(stack())
        stack() = result
      EndIf
    Else
      AddElement(stack())
      stack() = ValD(token)
    EndIf
  EndIf
  
  LastElement(stack())
  ProcedureReturn stack()
EndProcedure

; GUI
Enumeration
  #Window
  #ExpressionInput
  #ResultLabel
  #RPNLabel
  #CalculateButton
  #ClearButton
EndEnumeration

OpenWindow(#Window, 0, 0, 500, 250, "Calculator - Shunting Yard Algorithm", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

TextGadget(#PB_Any, 10, 10, 480, 20, "Enter expression (e.g., 3+4*2/(1-5)^2):")
StringGadget(#ExpressionInput, 10, 35, 480, 25, "3+4*2/(1-5)^2")
ButtonGadget(#CalculateButton, 10, 70, 100, 30, "Calculate")
ButtonGadget(#ClearButton, 120, 70, 100, 30, "Clear")
TextGadget(#PB_Any, 10, 110, 480, 20, "RPN (Postfix):")
TextGadget(#RPNLabel, 10, 135, 480, 25, "", #PB_Text_Border)
TextGadget(#PB_Any, 10, 170, 480, 20, "Result:")
TextGadget(#ResultLabel, 10, 195, 480, 40, "", #PB_Text_Border)

Repeat
  event = WaitWindowEvent()
  
  Select event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #CalculateButton
          Define expr.s = GetGadgetText(#ExpressionInput)
          Define rpn.s = ShuntingYard(expr)
          Define result.d = EvaluateRPN(rpn)
          
          SetGadgetText(#RPNLabel, rpn)
          SetGadgetText(#ResultLabel, StrD(result, 10))
          
        Case #ClearButton
          SetGadgetText(#ExpressionInput, "")
          SetGadgetText(#RPNLabel, "")
          SetGadgetText(#ResultLabel, "")
      EndSelect
      
    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver
considering there are lots of RPN codes and also a couple of pb samples on the forum it's perhaps not so hard for it to make.

or make an interpreter for purebasic.

Code: Select all

; PureBasic Interpreter written in PureBasic
; Supports: Define, Variables, Debug, If/Else/EndIf, For/To/Next, Arithmetic, Comparisons

EnableExplicit

Enumeration TokenType
  #TOKEN_EOF
  #TOKEN_NUMBER
  #TOKEN_STRING
  #TOKEN_IDENTIFIER
  #TOKEN_KEYWORD
  #TOKEN_OPERATOR
  #TOKEN_ASSIGN
  #TOKEN_COMMA
  #TOKEN_LPAREN
  #TOKEN_RPAREN
EndEnumeration

Structure Token
  type.i
  value.s
  numValue.d
EndStructure

Structure Variable
  name.s
  value.d
  stringValue.s
  isString.i
EndStructure

Structure Interpreter
  List tokens.Token()
  List variables.Variable()
  List output.s()
  tokenIndex.i
  errorMsg.s
EndStructure

; ==================== TOKENIZER ====================

Procedure.s TrimString(text.s)
  text = RTrim(text)
  text = LTrim(text)
  ProcedureReturn text
EndProcedure

Procedure.i IsKeyword(word.s)
  word = LCase(word)
  If word = "define" Or word = "debug" Or word = "if" Or word = "else" Or word = "endif"
    ProcedureReturn #True
  EndIf
  If word = "for" Or word = "to" Or word = "next" Or word = "while" Or word = "wend"
    ProcedureReturn #True
  EndIf
  If word = "str" Or word = "val" Or word = "and" Or word = "or" Or word = "not"
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure Tokenize(code.s, List tokens.Token())
  Define pos.i = 1
  Define length.i = Len(code)
  Define ch.s, nextCh.s
  Define word.s
  Define inString.i = #False
  Define stringContent.s
  
  While pos <= length
    ch = Mid(code, pos, 1)
    
    ; Skip whitespace
    If ch = " " Or ch = #TAB$ Or ch = #CR$ Or ch = #LF$
      pos + 1
      Continue
    EndIf
    
    ; Skip comments
    If ch = ";"
      While pos <= length And Mid(code, pos, 1) <> #LF$
        pos + 1
      Wend
      Continue
    EndIf
    
    ; String literal
    If ch = #DQUOTE$
      stringContent = ""
      pos + 1
      While pos <= length And Mid(code, pos, 1) <> #DQUOTE$
        stringContent + Mid(code, pos, 1)
        pos + 1
      Wend
      pos + 1 ; Skip closing quote
      
      AddElement(tokens())
      tokens()\type = #TOKEN_STRING
      tokens()\value = stringContent
      Continue
    EndIf
    
    ; Number
    If ch >= "0" And ch <= "9"
      word = ""
      While pos <= length
        ch = Mid(code, pos, 1)
        If (ch >= "0" And ch <= "9") Or ch = "."
          word + ch
          pos + 1
        Else
          Break
        EndIf
      Wend
      
      AddElement(tokens())
      tokens()\type = #TOKEN_NUMBER
      tokens()\value = word
      tokens()\numValue = ValD(word)
      Continue
    EndIf
    
    ; Identifier or Keyword
    If (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z") Or ch = "_"
      word = ""
      While pos <= length
        ch = Mid(code, pos, 1)
        If (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z") Or (ch >= "0" And ch <= "9") Or ch = "_" Or ch = "."
          word + ch
          pos + 1
        Else
          Break
        EndIf
      Wend
      
      ; Remove type suffix (.i, .f, .s, etc)
      Define dotPos.i = FindString(word, ".", 1)
      Define baseName.s = word
      If dotPos > 0
        baseName = Left(word, dotPos - 1)
      EndIf
      
      AddElement(tokens())
      If IsKeyword(baseName)
        tokens()\type = #TOKEN_KEYWORD
        tokens()\value = baseName
      Else
        tokens()\type = #TOKEN_IDENTIFIER
        tokens()\value = baseName
      EndIf
      Continue
    EndIf
    
    ; Two-character operators
    If pos < length
      nextCh = Mid(code, pos + 1, 1)
      Define twoChar.s = ch + nextCh
      If twoChar = "<=" Or twoChar = ">=" Or twoChar = "<>" Or twoChar = "=="
        AddElement(tokens())
        tokens()\type = #TOKEN_OPERATOR
        tokens()\value = twoChar
        pos + 2
        Continue
      EndIf
    EndIf
    
    ; Single character operators
    If ch = "+" Or ch = "-" Or ch = "*" Or ch = "/" Or ch = "<" Or ch = ">"
      AddElement(tokens())
      tokens()\type = #TOKEN_OPERATOR
      tokens()\value = ch
      pos + 1
      Continue
    EndIf
    
    ; Assignment
    If ch = "="
      AddElement(tokens())
      tokens()\type = #TOKEN_ASSIGN
      tokens()\value = "="
      pos + 1
      Continue
    EndIf
    
    ; Parentheses
    If ch = "("
      AddElement(tokens())
      tokens()\type = #TOKEN_LPAREN
      tokens()\value = "("
      pos + 1
      Continue
    EndIf
    
    If ch = ")"
      AddElement(tokens())
      tokens()\type = #TOKEN_RPAREN
      tokens()\value = ")"
      pos + 1
      Continue
    EndIf
    
    ; Comma
    If ch = ","
      AddElement(tokens())
      tokens()\type = #TOKEN_COMMA
      tokens()\value = ","
      pos + 1
      Continue
    EndIf
    
    ; Unknown character, skip it
    pos + 1
  Wend
  
  ; Add EOF token
  AddElement(tokens())
  tokens()\type = #TOKEN_EOF
EndProcedure

; ==================== INTERPRETER ====================

Procedure.i CurrentToken(*interp.Interpreter)
  If *interp\tokenIndex > ListSize(*interp\tokens())
    ProcedureReturn #TOKEN_EOF
  EndIf
  
  If SelectElement(*interp\tokens(), *interp\tokenIndex)
    ProcedureReturn *interp\tokens()\type
  EndIf
  
  ProcedureReturn #TOKEN_EOF
EndProcedure

Procedure Advance(*interp.Interpreter)
  *interp\tokenIndex + 1
EndProcedure

Procedure.s GetTokenValue(*interp.Interpreter)
  If SelectElement(*interp\tokens(), *interp\tokenIndex)
    ProcedureReturn *interp\tokens()\value
  EndIf
  ProcedureReturn ""
EndProcedure

Procedure.d GetTokenNumber(*interp.Interpreter)
  If SelectElement(*interp\tokens(), *interp\tokenIndex)
    ProcedureReturn *interp\tokens()\numValue
  EndIf
  ProcedureReturn 0
EndProcedure

Procedure.d GetVariable(*interp.Interpreter, name.s)
  ForEach *interp\variables()
    If LCase(*interp\variables()\name) = LCase(name)
      ProcedureReturn *interp\variables()\value
    EndIf
  Next
  ProcedureReturn 0
EndProcedure

Procedure.s GetVariableString(*interp.Interpreter, name.s)
  ForEach *interp\variables()
    If LCase(*interp\variables()\name) = LCase(name)
      If *interp\variables()\isString
        ProcedureReturn *interp\variables()\stringValue
      Else
        ProcedureReturn StrD(*interp\variables()\value)
      EndIf
    EndIf
  Next
  ProcedureReturn ""
EndProcedure

Procedure SetVariable(*interp.Interpreter, name.s, value.d)
  ForEach *interp\variables()
    If LCase(*interp\variables()\name) = LCase(name)
      *interp\variables()\value = value
      *interp\variables()\isString = #False
      ProcedureReturn
    EndIf
  Next
  
  AddElement(*interp\variables())
  *interp\variables()\name = name
  *interp\variables()\value = value
  *interp\variables()\isString = #False
EndProcedure

Procedure SetVariableString(*interp.Interpreter, name.s, value.s)
  ForEach *interp\variables()
    If LCase(*interp\variables()\name) = LCase(name)
      *interp\variables()\stringValue = value
      *interp\variables()\isString = #True
      ProcedureReturn
    EndIf
  Next
  
  AddElement(*interp\variables())
  *interp\variables()\name = name
  *interp\variables()\stringValue = value
  *interp\variables()\isString = #True
EndProcedure

Declare.d ParseExpression(*interp.Interpreter)

Procedure.d ParsePrimary(*interp.Interpreter)
  Define result.d
  Define tokenType.i = CurrentToken(*interp)
  
  If tokenType = #TOKEN_NUMBER
    result = GetTokenNumber(*interp)
    Advance(*interp)
    ProcedureReturn result
  EndIf
  
  If tokenType = #TOKEN_IDENTIFIER
    Define varName.s = GetTokenValue(*interp)
    Advance(*interp)
    ProcedureReturn GetVariable(*interp, varName)
  EndIf
  
  If tokenType = #TOKEN_LPAREN
    Advance(*interp)
    result = ParseExpression(*interp)
    If CurrentToken(*interp) = #TOKEN_RPAREN
      Advance(*interp)
    EndIf
    ProcedureReturn result
  EndIf
  
  ; Handle Str() function
  If tokenType = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "str"
    Advance(*interp)
    If CurrentToken(*interp) = #TOKEN_LPAREN
      Advance(*interp)
      result = ParseExpression(*interp)
      If CurrentToken(*interp) = #TOKEN_RPAREN
        Advance(*interp)
      EndIf
      ProcedureReturn result ; Return as number, will convert to string later
    EndIf
  EndIf
  
  ; Handle Val() function
  If tokenType = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "val"
    Advance(*interp)
    If CurrentToken(*interp) = #TOKEN_LPAREN
      Advance(*interp)
      result = ParseExpression(*interp)
      If CurrentToken(*interp) = #TOKEN_RPAREN
        Advance(*interp)
      EndIf
      ProcedureReturn result
    EndIf
  EndIf
  
  ProcedureReturn 0
EndProcedure

Procedure.d ParseMultiplicative(*interp.Interpreter)
  Define left.d = ParsePrimary(*interp)
  
  While CurrentToken(*interp) = #TOKEN_OPERATOR
    Define op.s = GetTokenValue(*interp)
    If op <> "*" And op <> "/"
      Break
    EndIf
    
    Advance(*interp)
    Define right.d = ParsePrimary(*interp)
    
    If op = "*"
      left = left * right
    ElseIf op = "/"
      If right <> 0
        left = left / right
      EndIf
    EndIf
  Wend
  
  ProcedureReturn left
EndProcedure

Procedure.d ParseAdditive(*interp.Interpreter)
  Define left.d = ParseMultiplicative(*interp)
  
  While CurrentToken(*interp) = #TOKEN_OPERATOR
    Define op.s = GetTokenValue(*interp)
    If op <> "+" And op <> "-"
      Break
    EndIf
    
    Advance(*interp)
    Define right.d = ParseMultiplicative(*interp)
    
    If op = "+"
      left = left + right
    ElseIf op = "-"
      left = left - right
    EndIf
  Wend
  
  ProcedureReturn left
EndProcedure

Procedure.d ParseComparison(*interp.Interpreter)
  Define left.d = ParseAdditive(*interp)
  
  While CurrentToken(*interp) = #TOKEN_OPERATOR
    Define op.s = GetTokenValue(*interp)
    If op <> "<" And op <> ">" And op <> "<=" And op <> ">=" And op <> "<>" And op <> "=="
      Break
    EndIf
    
    Advance(*interp)
    Define right.d = ParseAdditive(*interp)
    
    Select op
      Case "<"
        If left < right : left = 1 : Else : left = 0 : EndIf
      Case ">"
        If left > right : left = 1 : Else : left = 0 : EndIf
      Case "<="
        If left <= right : left = 1 : Else : left = 0 : EndIf
      Case ">="
        If left >= right : left = 1 : Else : left = 0 : EndIf
      Case "<>"
        If left <> right : left = 1 : Else : left = 0 : EndIf
      Case "=="
        If left = right : left = 1 : Else : left = 0 : EndIf
    EndSelect
  Wend
  
  ProcedureReturn left
EndProcedure

Procedure.d ParseExpression(*interp.Interpreter)
  ProcedureReturn ParseComparison(*interp)
EndProcedure

Declare ExecuteStatement(*interp.Interpreter)

Procedure ExecuteDefine(*interp.Interpreter)
  Advance(*interp) ; Skip Define keyword
  
  While CurrentToken(*interp) = #TOKEN_IDENTIFIER
    Define varName.s = GetTokenValue(*interp)
    SetVariable(*interp, varName, 0)
    Advance(*interp)
    
    If CurrentToken(*interp) = #TOKEN_COMMA
      Advance(*interp)
    Else
      Break
    EndIf
  Wend
EndProcedure

Procedure ExecuteAssignment(*interp.Interpreter)
  Define varName.s = GetTokenValue(*interp)
  Advance(*interp)
  
  If CurrentToken(*interp) = #TOKEN_ASSIGN
    Advance(*interp)
    Define value.d = ParseExpression(*interp)
    SetVariable(*interp, varName, value)
  EndIf
EndProcedure

Procedure ExecuteDebug(*interp.Interpreter)
  Advance(*interp) ; Skip Debug keyword
  
  ; Handle string literals
  If CurrentToken(*interp) = #TOKEN_STRING
    Define output.s = GetTokenValue(*interp)
    Advance(*interp)
    
    ; Check for string concatenation
    While CurrentToken(*interp) = #TOKEN_OPERATOR And GetTokenValue(*interp) = "+"
      Advance(*interp)
      If CurrentToken(*interp) = #TOKEN_STRING
        output + GetTokenValue(*interp)
        Advance(*interp)
      ElseIf CurrentToken(*interp) = #TOKEN_IDENTIFIER
        output + StrD(GetVariable(*interp, GetTokenValue(*interp)))
        Advance(*interp)
      ElseIf CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "str"
        Advance(*interp)
        If CurrentToken(*interp) = #TOKEN_LPAREN
          Advance(*interp)
          Define val.d = ParseExpression(*interp)
          output + StrD(val)
          If CurrentToken(*interp) = #TOKEN_RPAREN
            Advance(*interp)
          EndIf
        EndIf
      Else
        output + StrD(ParseExpression(*interp))
      EndIf
    Wend
    
    AddElement(*interp\output())
    *interp\output() = output
  Else
    Define value.d = ParseExpression(*interp)
    AddElement(*interp\output())
    *interp\output() = StrD(value)
  EndIf
EndProcedure

Procedure ExecuteIf(*interp.Interpreter)
  Advance(*interp) ; Skip If keyword
  Define condition.d = ParseExpression(*interp)
  
  ; Save positions for then and else blocks
  Define thenStart.i = *interp\tokenIndex
  Define elseStart.i = -1
  Define depth.i = 1
  
  ; Find Else and EndIf
  While CurrentToken(*interp) <> #TOKEN_EOF And depth > 0
    If CurrentToken(*interp) = #TOKEN_KEYWORD
      Define kw.s = LCase(GetTokenValue(*interp))
      If kw = "if"
        depth + 1
      ElseIf kw = "else" And depth = 1
        elseStart = *interp\tokenIndex + 1
      ElseIf kw = "endif"
        depth - 1
        If depth = 0
          Break
        EndIf
      EndIf
    EndIf
    Advance(*interp)
  Wend
  
  Define endPos.i = *interp\tokenIndex
  
  ; Execute appropriate block
  If condition <> 0
    *interp\tokenIndex = thenStart
    While *interp\tokenIndex < endPos
      If CurrentToken(*interp) = #TOKEN_KEYWORD
        kw = LCase(GetTokenValue(*interp))
        If kw = "else" Or kw = "endif"
          Break
        EndIf
      EndIf
      ExecuteStatement(*interp)
    Wend
  ElseIf elseStart >= 0
    *interp\tokenIndex = elseStart
    While *interp\tokenIndex < endPos
      If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "endif"
        Break
      EndIf
      ExecuteStatement(*interp)
    Wend
  EndIf
  
  *interp\tokenIndex = endPos
  If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "endif"
    Advance(*interp)
  EndIf
EndProcedure

Procedure ExecuteFor(*interp.Interpreter)
  Advance(*interp) ; Skip For keyword
  
  Define loopVar.s = GetTokenValue(*interp)
  Advance(*interp)
  
  If CurrentToken(*interp) = #TOKEN_ASSIGN
    Advance(*interp)
  EndIf
  
  Define startVal.d = ParseExpression(*interp)
  
  If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "to"
    Advance(*interp)
  EndIf
  
  Define endVal.d = ParseExpression(*interp)
  Define loopStart.i = *interp\tokenIndex
  
  ; Find matching Next
  Define depth.i = 1
  While CurrentToken(*interp) <> #TOKEN_EOF And depth > 0
    If CurrentToken(*interp) = #TOKEN_KEYWORD
      Define kw.s = LCase(GetTokenValue(*interp))
      If kw = "for"
        depth + 1
      ElseIf kw = "next"
        depth - 1
        If depth = 0
          Break
        EndIf
      EndIf
    EndIf
    Advance(*interp)
  Wend
  
  Define loopEnd.i = *interp\tokenIndex
  
  ; Execute loop
  Define i.d
  While i < endVal
    SetVariable(*interp, loopVar, i)
    *interp\tokenIndex = loopStart
    While *interp\tokenIndex < loopEnd
      ExecuteStatement(*interp)
    Wend
    i+1  
  Wend 
  
  *interp\tokenIndex = loopEnd
  If CurrentToken(*interp) = #TOKEN_KEYWORD And LCase(GetTokenValue(*interp)) = "next"
    Advance(*interp)
    If CurrentToken(*interp) = #TOKEN_IDENTIFIER
      Advance(*interp)
    EndIf
  EndIf
EndProcedure

Procedure ExecuteStatement(*interp.Interpreter)
  If CurrentToken(*interp) = #TOKEN_EOF
    ProcedureReturn
  EndIf
  
  If CurrentToken(*interp) = #TOKEN_KEYWORD
    Define kw.s = LCase(GetTokenValue(*interp))
    
    Select kw
      Case "define"
        ExecuteDefine(*interp)
      Case "debug"
        ExecuteDebug(*interp)
      Case "if"
        ExecuteIf(*interp)
      Case "for"
        ExecuteFor(*interp)
      Default
        Advance(*interp)
    EndSelect
  ElseIf CurrentToken(*interp) = #TOKEN_IDENTIFIER
    ExecuteAssignment(*interp)
  Else
    Advance(*interp)
  EndIf
EndProcedure

Procedure Execute(*interp.Interpreter)
  While CurrentToken(*interp) <> #TOKEN_EOF
    ExecuteStatement(*interp)
  Wend
EndProcedure

; ==================== MAIN PROGRAM ====================

Define code.s
code = "; PureBasic Interpreter Test" + #LF$
code + "Define x, y, result" + #LF$
code + "x = 10" + #LF$
code + "y = 20" + #LF$
code + "result = x + y" + #LF$
code + "Debug " + #DQUOTE$ + "Sum: " + #DQUOTE$ + " + Str(result)" + #LF$
code + #LF$
code + "If result > 25" + #LF$
code + "  Debug " + #DQUOTE$ + "Result is greater than 25" + #DQUOTE$ + #LF$
code + "Else" + #LF$
code + "  Debug " + #DQUOTE$ + "Result is 25 or less" + #DQUOTE$ + #LF$
code + "EndIf" + #LF$
code + #LF$
code + "For i = 1 To 5" + #LF$
code + "  Debug " + #DQUOTE$ + "Loop iteration: " + #DQUOTE$ + " + Str(i)" + #LF$
code + "Next i" + #LF$

Debug "=== PureBasic Interpreter ===" + #LF$
Debug "Input Code:"
Debug code
Debug ""
Debug "=== Execution Output ==="

Define interp.Interpreter
Tokenize(code, interp\tokens())
Execute(@interp)

ForEach interp\output()
  Debug interp\output()
Next

If interp\errorMsg <> ""
  Debug "Error: " + interp\errorMsg
EndIf
or Breakout

Code: Select all

; Breakout Game in PureBasic
; Use arrow keys to move paddle, SPACE to launch ball, ESC to quit

#WINDOW_WIDTH = 640
#WINDOW_HEIGHT = 480
#PADDLE_WIDTH = 100
#PADDLE_HEIGHT = 15
#BALL_SIZE = 10
#BRICK_WIDTH = 60
#BRICK_HEIGHT = 20
#BRICK_ROWS = 5
#BRICK_COLS = 10
#BRICK_PADDING = 5

Structure Brick
  x.i
  y.i
  active.i
  color.i
EndStructure

Global Dim bricks.Brick(#BRICK_ROWS * #BRICK_COLS)
Global paddleX.f = (#WINDOW_WIDTH - #PADDLE_WIDTH) / 2
Global ballX.f = #WINDOW_WIDTH / 2
Global ballY.f = #WINDOW_HEIGHT - 100
Global ballSpeedX.f = 0
Global ballSpeedY.f = 0
Global ballLaunched.i = #False
Global score.i = 0
Global lives.i = 3

Procedure InitBricks()
  Define i, row, col, idx
  Define startY = 50
  
  For row = 0 To #BRICK_ROWS - 1
    For col = 0 To #BRICK_COLS - 1
      idx = row * #BRICK_COLS + col
      bricks(idx)\x = col * (#BRICK_WIDTH + #BRICK_PADDING) + 20
      bricks(idx)\y = row * (#BRICK_HEIGHT + #BRICK_PADDING) + startY
      bricks(idx)\active = #True
      
      ; Different colors for different rows
      Select row
        Case 0: bricks(idx)\color = RGB(255, 0, 0)     ; Red
        Case 1: bricks(idx)\color = RGB(255, 165, 0)   ; Orange
        Case 2: bricks(idx)\color = RGB(255, 255, 0)   ; Yellow
        Case 3: bricks(idx)\color = RGB(0, 255, 0)     ; Green
        Case 4: bricks(idx)\color = RGB(0, 0, 255)     ; Blue
      EndSelect
    Next
  Next
EndProcedure

Procedure ResetBall()
  ballX = paddleX + #PADDLE_WIDTH / 2
  ballY = #WINDOW_HEIGHT - 100
  ballSpeedX = 0
  ballSpeedY = 0
  ballLaunched = #False
EndProcedure

Procedure Clamp(x,y,z) 
  Protected res = x 
  If x < y 
    res = y 
  ElseIf x > z 
    res = z 
  EndIf 
  ProcedureReturn res 
  
EndProcedure   

Procedure CheckCollisions()
  Define i, brickRight, brickBottom
  
  ; Ball collision with walls
  If ballX <= 0 Or ballX >= #WINDOW_WIDTH - #BALL_SIZE
    ballSpeedX = -ballSpeedX
    ballX = Clamp(ballX, 0, #WINDOW_WIDTH - #BALL_SIZE)
  EndIf
  
  If ballY <= 0
    ballSpeedY = -ballSpeedY
    ballY = 0
  EndIf
  
  ; Ball falls below paddle - lose life
  If ballY >= #WINDOW_HEIGHT
    lives - 1
    If lives > 0
      ResetBall()
    EndIf
  EndIf
  
  ; Ball collision with paddle
  If ballY + #BALL_SIZE >= #WINDOW_HEIGHT - 50 And 
     ballY + #BALL_SIZE <= #WINDOW_HEIGHT - 35 And
     ballX + #BALL_SIZE >= paddleX And 
     ballX <= paddleX + #PADDLE_WIDTH
    
    ballSpeedY = -Abs(ballSpeedY)
    ; Add spin based on where ball hits paddle
    Define hitPos.f = (ballX - paddleX) / #PADDLE_WIDTH
    ballSpeedX = (hitPos - 0.5) * 8
  EndIf
  
  ; Ball collision with bricks
  For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
    If bricks(i)\active
      brickRight = bricks(i)\x + #BRICK_WIDTH
      brickBottom = bricks(i)\y + #BRICK_HEIGHT
      
      If ballX + #BALL_SIZE >= bricks(i)\x And 
         ballX <= brickRight And
         ballY + #BALL_SIZE >= bricks(i)\y And 
         ballY <= brickBottom
        
        bricks(i)\active = #False
        ballSpeedY = -ballSpeedY
        score + 10
        Break
      EndIf
    EndIf
  Next
EndProcedure
 
; Main program
InitSprite()
InitKeyboard() 

If OpenWindow(0, 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, "Breakout Game", #PB_Window_ScreenCentered)
  If OpenWindowedScreen(WindowID(0), 0, 0, #WINDOW_WIDTH, #WINDOW_HEIGHT, 0, 0, 0)
    
    InitBricks()
    
    Repeat
      ; Handle input
      Repeat
        Define event = WindowEvent()
        If event = #PB_Event_CloseWindow
          Break 2
        EndIf
      Until event = 0
      
      ; Keyboard input
      ExamineKeyboard()
      
      If KeyboardPushed(#PB_Key_Escape)
        Break
      EndIf
      
      If KeyboardPushed(#PB_Key_Left) And paddleX > 0
        paddleX - 8
      EndIf
      
      If KeyboardPushed(#PB_Key_Right) And paddleX < #WINDOW_WIDTH - #PADDLE_WIDTH
        paddleX + 8
      EndIf
      
      If KeyboardPushed(#PB_Key_Space) And Not ballLaunched
        ballLaunched = #True
        ballSpeedX = Random(2) - 1
        ballSpeedY = -5
      EndIf
      
      ; Update ball position
      If ballLaunched
        ballX + ballSpeedX
        ballY + ballSpeedY
        CheckCollisions()
      Else
        ; Ball follows paddle before launch
        ballX = paddleX + #PADDLE_WIDTH / 2 - #BALL_SIZE / 2
      EndIf
      
      ; Check for game over or win
      Define allBricksGone = #True
      For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
        If bricks(i)\active
          allBricksGone = #False
          Break
        EndIf
      Next
      
      ; Draw everything
      ClearScreen(RGB(0, 0, 0))
      StartDrawing(ScreenOutput())
      ; Draw bricks
      For i = 0 To #BRICK_ROWS * #BRICK_COLS - 1
        If bricks(i)\active
          Box(bricks(i)\x, bricks(i)\y, #BRICK_WIDTH, #BRICK_HEIGHT, bricks(i)\color)
        EndIf
      Next
      
      ; Draw paddle
      Box(paddleX, #WINDOW_HEIGHT - 50, #PADDLE_WIDTH, #PADDLE_HEIGHT, RGB(200, 200, 200))
      
      ; Draw ball
      Circle(ballX + #BALL_SIZE / 2, ballY + #BALL_SIZE / 2, #BALL_SIZE / 2, RGB(255, 255, 255))
      
      ; Draw score and lives
      DrawText(10, 10, "Score: " + Str(score), RGB(255, 255, 255))
      DrawText(#WINDOW_WIDTH - 100, 10, "Lives: " + Str(lives), RGB(255, 255, 255))
      
      ; Check win/lose conditions
      If lives <= 0
        DrawText(#WINDOW_WIDTH / 2 - 80, #WINDOW_HEIGHT / 2, "GAME OVER! Press ESC", RGB(255, 0, 0))
      ElseIf allBricksGone
        DrawText(#WINDOW_WIDTH / 2 - 80, #WINDOW_HEIGHT / 2, "YOU WIN! Press ESC", RGB(0, 255, 0))
      EndIf
      StopDrawing()
      FlipBuffers()
      Delay(16) ; ~60 FPS
      
    ForEver
    
    CloseScreen()
  EndIf
  CloseWindow(0)
EndIf
End
User avatar
STARGÅTE
Addict
Addict
Posts: 2275
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Making simple apps with AI

Post by STARGÅTE »

idle wrote: Mon Dec 29, 2025 9:19 pm asked claude
or make an interpreter for purebasic.
Is this an example for or against KI generated code?
For me, such generated codes remain a nice gimmick, but have no practical use.
You can't really use something like that, can you?

Regarding the code itself:
  • Using Define in a procedure is not recommended. In procedure you have to use Protected instead.
  • TrimString() is reduntand, because we have Trim() already.
  • Using comparison operators like >=, >, <, and <= with strings are not official documented by Pure Basic.
Regarding the interpreter:
  • The interpreter uses == als comparison operator instead of =
  • The For i = 1 To 5 loop iterates from 0 to 4 and ignores the start value, because startVal.d is never used in ExecuteFor().
  • While, Wend, And, Or, and Not are interpreted as keywords, but not evaluated.
I mean, the generated codes do not have to be complete, but they should be self-consistent.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Post Reply