The prompt journey from Structured Basic

(from first prompt to full parameter-stack model)


Prompt 1

User:

write a C++ program that parses a basic program and replaces all occurrences if {} wrapped names with a line number

Assistant:

  • Started with a C++ scanner that replaced {label} placeholders with line numbers.
  • Example:

Structured BASIC

PRINT "Welcome"
GOTO {start}

{start}: PRINT "Go!"

Generated BASIC

PRINT "Welcome"
GOTO 10

10 PRINT "Go!"

Prompt 2

User:

the input should not have line numbers
yes, and maybe we can just have labels without the {} like loop: and gosub loop

Assistant:

  • Switched to plain label: syntax.
  • Example:

Structured BASIC

PRINT "Welcome"
GOTO start

start:
  PRINT "Go!"

Generated BASIC

PRINT "Welcome"
GOTO 10

10 
  PRINT "Go!"

Prompt 3

User:

support multi liine if/else/end and collcapse them on output into concatencated : statements

Assistant:

  • Multi-line IF lowered to GOTOs.

Structured BASIC

IF X > 0 THEN
  PRINT "Positive"
ELSE
  PRINT "Negative"
END IF

Generated BASIC

IF X > 0 THEN GOTO 10
GOTO 20

10 
  PRINT "Positive"
  GOTO 30

20 
  PRINT "Negative"

30 

Prompt 4

User:

actually to support multi-line if/else/end use goto, also add support for switch also using gotos

Assistant:

  • SWITCH lowered to chained IFs and labels.

Structured BASIC

SWITCH K
  CASE 1:
    PRINT "one"
    BREAK
  CASE 2:
    PRINT "two"
  DEFAULT:
    PRINT "other"
END SWITCH

Generated BASIC

IF K = 1 THEN GOTO 10
IF K = 2 THEN GOTO 20
GOTO 30

10 
  PRINT "one"
  GOTO 40

20 
  PRINT "two"
  REM fall-through

30 
  PRINT "other"

40 

Prompt 5

User:

format the inout and output nicely
add indentation

Assistant:

  • Added indentation, labels on their own lines.

Prompt 6

User:

remember basic variables have a prefix indicating their type
was it typically prefix or suffix

Assistant:

  • Classic MS BASIC used suffix typing: $ strings, % integers, # doubles.
  • We kept suffixes.

Prompt 7

User:

what about user defined types were they available in compiled basic

Assistant:

  • In QuickBASIC, yes (TYPE).
  • In 1985 interpreted BASIC, no. Records simulated with FIELD.

Structured BASIC

RECORD Customer LEN = 40
  Id      AS STRING * 5
  Name    AS STRING * 20
  Balance AS STRING * 15
END RECORD

Generated BASIC

REM RECORD Customer (LEN=40)
REM   Customer_Id$      (5)
REM   Customer_Name$    (20)
REM   Customer_Balance$ (15)

Prompt 8

User:

let’s make all labels on their own lines
maybe we add a BLOCK keyword, you can GOTO that, and a SUB keyword you can GOSUB to that

Assistant:

  • BLOCK = jump target.
  • SUB = callable.
  • CALL replaces GOSUB.

Structured BASIC

PRINT "Boot"
GOTO init

BLOCK init
  PRINT "Initialising…"
END BLOCK

SUB greet
  PRINT "Hello"
END SUB

main:
  CALL greet
  GOTO done

done:
  PRINT "Bye"

Generated BASIC

PRINT "Boot"
GOTO 10

10 
  REM BLOCK init
  PRINT "Initialising…"

20 
  REM SUB greet
  PRINT "Hello"
  RETURN

30 
  REM main
  GOSUB 20
  GOTO 40

40 
  REM done
  PRINT "Bye"

Prompt 9

User:

convter this to the new structured basic program gbackup;

Assistant:

  • Produced a structured BASIC sketch of gbackup, using BLOCK, SUB, CALL, RECORD, IF, SWITCH.

Prompt 10

User:

let’s assume we have stacks for each parameter passed to a function (subroutine) show how the genereated basic would look

Assistant:

  • First attempt: separate stack per parameter.

Prompt 11

User:

let’s assume a generatlised set of stacks, one for $ one for #, one for record types and stack pointers for each, also use the stack for return values

Assistant:

  • Unified three stacks: S$(), N#(), R$().
  • Push args → GOSUB → callee pops → pushes return → caller pops.

Prompt 12

User:

i don’t see multiple return values or them being assigned

Assistant:

  • Example: LoadCustomer returns three values.

Structured BASIC

(Name$, Balance#, Exists#) = CALL LoadCustomer("1")

Generated BASIC

__argrec$ = "1" : GOSUB 940   ' PUSH_R
GOSUB 200
GOSUB 930 : Exists#  = __ret#
GOSUB 930 : Balance# = __ret#
GOSUB 910 : Name$    = __ret$

Callee:

200 ' LoadCustomer
GOSUB 950 : recKey$ = __retrec$
__arg$ = Name$    : GOSUB 900
__arg# = Balance# : GOSUB 920
__arg# = Exists#  : GOSUB 920
RETURN

Prompt 13

User:

where are the generalised push, pop macros

Assistant:

  • Gave macro and helper-sub approaches.

Prompt 14

User:

pure classic basic please

Assistant:

  • Showed helper subs at line numbers 900–950.
900  S_sp% = S_sp% + 1 : S$(S_sp%) = __arg$ : RETURN
910  __ret$ = S$(S_sp%) : S_sp% = S_sp% - 1 : RETURN

920  N_sp% = N_sp% + 1 : N#(N_sp%) = __arg# : RETURN
930  __ret# = N#(N_sp%) : N_sp% = N_sp% - 1 : RETURN

940  R_sp% = R_sp% + 1 : R$(R_sp%) = __argrec$ : RETURN
950  __retrec$ = R$(R_sp%) : R_sp% = R_sp% - 1 : RETURN

Final Summary

  • Structured Dialect: modern-looking BASIC with BLOCK, SUB, CALL, IF/ELSE/END IF, SWITCH, and RECORD.
  • Generated Classic BASIC: numbered labels, only GOTOs/GOSUBs, FIELD-based records, helper subs for argument/return passing.
  • Generalised Stacks: unified per-type stacks (S$, N#, R$) used for all argument and return passing.
  • Multiple Returns: supported by pushing values left-to-right, popping right-to-left per type.
  • Pure Classic: fully runnable in GW-BASIC/QuickBASIC with the helper subs at 900–950.

This closes the loop: starting from {label} placeholders, we now have a structured BASIC dialect with modern constructs, compiled down to classic line-numbered BASIC with a defined calling convention.

Example

Structured BASIC (Input)

' ==============================================
' Structured BASIC demo (new dialect)
' Showcases: LABEL, GOTO, IF/ELSE, SWITCH,
' BLOCK, SUB, CALL, RECORD, multiple returns.
' ==============================================

PRINT "Program start"
GOTO init

' --- BLOCK is just a named jump target
BLOCK init
  PRINT "Initialising..."
END BLOCK

' --- RECORD declaration (syntactic sugar for FIELD)
RECORD Customer LEN = 40
  Id      AS STRING * 5
  Name    AS STRING * 20
  Balance AS STRING * 15
END RECORD

' --- SUB with parameters and return value
SUB Remount(mountPoint$, rw%)
  PRINT "Remounting "; mountPoint$; " rw="; rw%
  RETURN 0
END SUB

' --- SUB with multiple return values (mixed types)
SUB LoadCustomer(recRef$)
  Name$ = "Alice" : Balance# = 1234.56 : Exists# = 1
  RETURN Name$, Balance#, Exists#
END SUB

' --- SUB with multiple return values (same type)
SUB SplitName(full$)
  p% = INSTR(full$, " ")
  IF p% > 0 THEN
    First$ = LEFT$(full$, p%-1)
    Last$  = MID$(full$, p%+1)
  ELSE
    First$ = full$
    Last$  = ""
  END IF
  RETURN First$, Last$
END SUB

main:
  PRINT "In main"

  ' --- CALL with params + numeric return
  rc# = CALL Remount("/", 1)
  IF rc# = 0 THEN
    PRINT "Remount succeeded"
  ELSE
    PRINT "Remount failed"
  END IF

  ' --- CALL with multiple returns (mixed types)
  (Name$, Balance#, Exists#) = CALL LoadCustomer("1")
  PRINT "Loaded:"; Name$, Balance#, Exists#

  ' --- SWITCH lowered with gotos
  SWITCH Mode$
    CASE "debug":
      PRINT "Debug mode"
      BREAK
    CASE "safe":
      PRINT "Safe mode"
    DEFAULT:
      PRINT "Normal mode"
  END SWITCH

  ' --- CALL with multiple returns (same type)
  (First$, Last$) = CALL SplitName("Alice Bob")
  PRINT "First="; First$; " Last="; Last$

  GOTO done

done:
  PRINT "Goodbye"

Generated Classic BASIC (Output)

REM ==============================================
REM Generated BASIC (classic dialect)
REM Demonstrates how structured features are lowered
REM ==============================================

' --- Global type stacks (for params and returns)
DIM S$(256): S_sp% = 0   ' String stack
DIM N#(256): N_sp% = 0   ' Numeric stack
DIM R$(256): R_sp% = 0   ' Record refs (string)
' --- Helpers (PUSH/POP subs) must exist at 900–950

PRINT "Program start"
GOTO 10

10 
  REM BLOCK init
  PRINT "Initialising..."

REM RECORD Customer (LEN=40)
REM   Customer_Id$      (5)
REM   Customer_Name$    (20)
REM   Customer_Balance$ (15)

' ==============================================
' SUB Remount(mountPoint$, rw%) -> returns rc#
' ==============================================
100 
GOSUB 930 : rw#         = __ret#   ' POP numeric param
GOSUB 910 : mountPoint$ = __ret$   ' POP string param
PRINT "Remounting "; mountPoint$; " rw="; rw#
__arg# = 0 : GOSUB 920             ' push return rc#
RETURN

' ==============================================
' SUB LoadCustomer(recRef$)
' returns: Name$ (S$), Balance# (N#), Exists# (N#)
' ==============================================
200 
GOSUB 950 : recRef$ = __retrec$    ' POP record ref
Name2$    = "Alice"
Balance2# = 1234.56
Exists2#  = 1
__arg$ = Name2$    : GOSUB 900     ' push returns L→R
__arg# = Balance2# : GOSUB 920
__arg# = Exists2#  : GOSUB 920
RETURN

' ==============================================
' SUB SplitName(full$)
' returns: First$ (S$), Last$ (S$)
' ==============================================
300 
GOSUB 910 : full$ = __ret$         ' POP string param
p% = INSTR(full$, " ")
IF p% > 0 THEN
  First2$ = LEFT$(full$, p%-1)
  Last2$  = MID$(full$, p%+1)
ELSE
  First2$ = full$
  Last2$  = ""
END IF
__arg$ = First2$ : GOSUB 900       ' push returns
__arg$ = Last2$  : GOSUB 900
RETURN

' ==============================================
' main:
' ==============================================
400 
PRINT "In main"

' CALL Remount("/", 1)
__arg$ = "/" : GOSUB 900
__arg# = 1   : GOSUB 920
GOSUB 100
GOSUB 930 : rc# = __ret#

IF rc# = 0 THEN GOTO 410
GOTO 420

410 
  PRINT "Remount succeeded"
  GOTO 430

420 
  PRINT "Remount failed"

430 
' CALL LoadCustomer("1")
__argrec$ = "1" : GOSUB 940
GOSUB 200
GOSUB 930 : Exists#  = __ret#
GOSUB 930 : Balance# = __ret#
GOSUB 910 : Name$    = __ret$
PRINT "Loaded:"; Name$, Balance#, Exists#

' SWITCH Mode$ -> chained IFs
IF Mode$ = "debug" THEN GOTO 440
IF Mode$ = "safe"  THEN GOTO 450
GOTO 460

440 
  PRINT "Debug mode"
  GOTO 470

450 
  PRINT "Safe mode"
  REM fall-through to default

460 
  PRINT "Normal mode"

470 
' CALL SplitName("Alice Bob")
__arg$ = "Alice Bob" : GOSUB 900
GOSUB 300
GOSUB 910 : Last$  = __ret$
GOSUB 910 : First$ = __ret$
PRINT "First="; First$; " Last="; Last$

GOTO 480

480 
  REM done
  PRINT "Goodbye"
END

' ==============================================
' Helper subs (classic BASIC, 900–950)
' ==============================================
900  S_sp% = S_sp% + 1 : S$(S_sp%) = __arg$ : RETURN
910  __ret$ = S$(S_sp%) : S_sp% = S_sp% - 1 : RETURN
920  N_sp% = N_sp% + 1 : N#(N_sp%) = __arg# : RETURN
930  __ret# = N#(N_sp%) : N_sp% = N_sp% - 1 : RETURN
940  R_sp% = R_sp% + 1 : R$(R_sp%) = __argrec$ : RETURN
950  __retrec$ = R$(R_sp%) : R_sp% = R_sp% - 1 : RETURN

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.