La structure d'un programme

Écrire un programme que le compilateur accepte : lignes et deux-points, les six formes de commentaire, noms et casse, chaînes, la règle des parenthèses, et les garde-fous qui refusent une ligne.

En un mot#

Un programme BASIC est une liste de lignes. Chaque ligne porte une instruction, ou plusieurs séparées par :. Le compilateur lit tout le programme avant de le lancer, et il refuse une ligne qu'il ne comprend pas au lieu de deviner.

Ce chapitre traite de la forme d'un programme. Les valeurs qu'il manipule sont dans Variables et types, et les opérateurs dans Constantes et opérateurs.

Lignes et instructions#

<statement>
<statement> : <statement> [: <statement> ...]
<label>:
<label>: <statement> [: <statement> ...]
x = 10
y = 20 : total = x + y
Console(Str(total))   ' 30
  • Une instruction par ligne, ou plusieurs séparées par :.
  • L'indentation est libre. Les espaces et tabulations en début de ligne ne comptent pas. Les lignes vides sont permises partout.
  • Un : dans une chaîne ou entre parenthèses ne coupe pas la ligne : "time: 12:30" reste une seule chaîne.
  • Un bloc peut tenir sur une ligne : FOR i = 1 TO 5 : total = total + i : NEXT.

Un IF sur une ligne garde tout ce qui suit THEN#

Après THEN, chaque : appartient au IF. Toutes les instructions ne s'exécutent que si la condition est vraie.

lives = 0
IF lives = 0 THEN Console("Partie perdue") : lives = 3
Console(Str(lives))   ' 3

Le détail est dans Le contrôle du flux.

Les étiquettes#

Un nom suivi de : en début de ligne est une étiquette, la cible de GOSUB et de GOTO. L'étiquette peut porter des instructions sur la même ligne.

GOSUB Greet
Console("retour")   ' affiché en second
END
Greet: Console("bonjour") : RETURN

⚠️ À cause de cette règle, un : placé juste après le premier mot d'une ligne déclare toujours une étiquette. start: x = 1 est l'étiquette start, avec x = 1 pour corps, et le compilateur réclame son RETURN : BASIC label start requires RETURN.

Ce qui n'existe pas#

Usage classiqueDans aukimi
Numéros de ligne (10 x = 1)refusés : Not a BASIC statement
Suite de ligne (_ en fin de ligne)refusée : écrivez l'instruction sur une ligne
; comme commentairece n'est pas un commentaire : la suite est compilée comme du code

Une ligne qui n'est qu'une clôture de bloc de code Markdown (trois accents graves, avec ou sans nom de langage) est ignorée. Du code copié d'une page web avec sa clôture tourne quand même, et ses numéros de ligne restent justes.

Les commentaires#

Six formes sont acceptées.

FormeOùExemple
'partout ; le reste de la ligne est un commentairex = 1 ' note
//partout ; le reste de la ligne est un commentairex = 1 // note
REMen début de ligne, ou après une instructionREM note
` (accent grave)en début de ligne, ou après une instructionx = 1 ` note
REMSTART … REMENDbloc ; chacun des deux mots ouvre sa lignevoir plus bas
/* … */bloc ; /* ouvre sa lignevoir plus bas
' Un commentaire sur toute la ligne
REM Un autre commentaire sur toute la ligne
// Un commentaire de ligne à la manière du C
x = 1   ' après une instruction
y = 2   // après une instruction aussi
z = 3   ` la forme accent grave
REMSTART
Un bloc de notes
sur plusieurs lignes
REMEND
/* Un bloc à la manière du C
   sur plusieurs lignes */
Console(Str(x + y + z))   ' 6

Utilisez ' ou //. Ce sont les deux seules formes sûres partout. Les quatre autres existent pour que le code venu d'autres dialectes compile encore.

⚠️ Un : dans un commentaire REM ou accent grave coupe la ligne#

Le découpage des lignes s'arrête à ' et à //. Il ne s'arrête ni à REM ni à l'accent grave. Un deux-points placé après eux termine le commentaire, et la suite est compilée comme du code.

x = 1 REM note: this part is compiled
Not a BASIC statement: this part is compiled

Il en va de même pour un commentaire accent grave sur toute la ligne qui contient un deux-points. Un REM sur toute la ligne est sûr ; un REM après une instruction ne l'est pas.

⚠️ Les commentaires en bloc ont trois règles#

  1. REMSTART, REMEND et /* ouvrent leur ligne. x = 1 REMSTART n'est pas un commentaire en bloc.

  2. Tout ce qui est sur la ligne de /* est commentaire, et la fin de ligne après */ aussi. Le code écrit là ne s'exécute jamais :

    /* setup */ Console("jamais affiché")
    Console("affiché")   ' affiché
  3. Une ligne dans un bloc ne doit pas commencer par un seul mot suivi de :. Le compilateur lit TODO: ou Note: comme une étiquette et refuse le programme.

    REMSTART
    TODO: tidy this up
    REMEND
    BASIC label TODO requires RETURN
    

    Écrivez TODO - à ranger, ou utilisez des lignes '.

Un bloc jamais refermé est une erreur : REMSTART requires REMEND, ou /* requires */.

Casse et noms#

Tout est insensible à la casse : mots-clés, noms de commandes, variables, fonctions utilisateur, champs d'enregistrement et constantes. IF, If et if sont le même mot. Console, CONSOLE et console sont la même commande.

La première graphie d'un nom est conservée, et toutes les suivantes sont réécrites sur elle.

Score = 1
SCORE = SCORE + 1
score = score + 1
Console(Str(sCoRe))   ' 3
if score = 3 then Console("les mots-clés en minuscules marchent aussi")

Deux exceptions :

NomRègle
PIen majuscules seulement. pi ou Pi est un nom inconnu : le programme s'arrête sur … is not defined.
TRUE, FALSE, NULLn'importe quelle casse.
area = PI * 2 ^ 2
Console(Str(area))   ' 12.566370614359172

Écrire un nom#

  • Des lettres, des chiffres et _. Il commence par une lettre ou par _.
  • Une variable peut finir par un sigil : $, #, % ou &. Ce que fait un sigil est dans Variables et types.
  • Une constante s'écrit avec # devant : #MAX_LIVES. Voir Constantes et opérateurs.

Quels noms peuvent partager une graphie#

PaireMême graphie permise ?
une variable et un champ d'enregistrement (speed et s.speed)oui, ils ne se croisent jamais
une variable et une FUNCTION utilisateuroui
une variable et une commande du catalogue (left et Left)oui, dès que le programme affecte la variable par nom = …
une FUNCTION utilisateur et une commande du catalogueoui, et la fonction utilisateur l'emporte
une constante et une variable (#SPEED et SPEED)⚠️ non : écrites pareil, le programme ne démarre pas
TYPE Ship
  speed
ENDTYPE
s AS Ship
s.speed = 4
speed = 9
Console(Str(s.speed) + " " + Str(speed))   ' 4 9
left = 10
x = left + 1
Console(Str(x) + Left("abc", 1))   ' 11a
FUNCTION Sin(v)
ENDFUNCTION v * 2
Console(Str(Sin(90)))   ' 180 : votre Sin, pas celui du catalogue

⚠️ Les mots de JavaScript ne sont pas libres#

Le BASIC se compile en JavaScript. Une variable qui porte le nom d'un mot-clé JavaScript compile, mais le programme ne démarre pas.

class = 3

Évitez ces noms : class, const, debugger, default, enum, export, extends, import, in, instanceof, let, new, super, switch, this, throw, typeof, var, void, await. Évitez aussi les noms qui commencent par _b ou __basic, et nextFrame : le compilateur s'en sert pour lui-même.

Les chaînes#

"<text>"
  • Guillemets droits doubles seulement. Un ' ouvre un commentaire, donc 'texte' n'est pas une chaîne.
  • Deux échappements existent : \" pour un guillemet, et \\ pour un antislash.
  • Tout autre antislash est un antislash ordinaire. Un chemin Windows marche tel quel, et \n fait deux caractères, pas un retour à la ligne.
  • Une chaîne tient sur une ligne.
path$ = "media\background5.jpg"
quote$ = "Elle a dit \"salut\""
slash$ = "a\\b"
Console(path$)    ' media\background5.jpg
Console(quote$)   ' Elle a dit "salut"
Console(slash$)   ' a\b

Pour un retour à la ligne ou un guillemet, construisez la chaîne avec Chr :

twoLines$ = "première" + Chr(10) + "seconde"
quoted$ = Chr(34) + "aukimi" + Chr(34)
Console(quoted$)   ' "aukimi"

⚠️ Le guillemet doublé de QBasic et de Visual Basic ("") n'est pas pris en charge. Il compile, mais le programme ne démarre pas. Écrivez \" ou Chr(34).

say$ = "say ""hi"""

⚠️ Après un \", ne mettez ni : ni ' dans la même chaîne. Le découpage des lignes ne connaît pas l'échappement : il croit que la chaîne s'arrête là.

label$ = "a\"b: c"
Not a BASIC statement: c"

Utilisez Chr(34) dans une telle chaîne. Les chaînes à {trous} s'écrivent $"…" : voir Les raccourcis.

Les littéraux#

SorteExemples
entier42, -7
décimal3.5, .5, 5.
exposant1.25e2, 1.5E-3
hexadécimal0x1F, 0XFF
octal0c17
binaire%1010
chaîne"texte"
vérité et absenceTRUE, FALSE, NULL
πPI
d = 1.25e2
e = 0x1F
f = 0c17
g = %1010
Console(Str(d) + " " + Str(e) + " " + Str(f) + " " + Str(g))   ' 125 31 15 10

Le séparateur décimal est le point, jamais la virgule. Un littéral mal formé est refusé sur sa ligne : Malformed BASIC numeric literal: 0x1G.

⚠️ $FF (PureBasic) et &HFF (QBasic, Visual Basic) ne sont pas de l'hexadécimal ici. Écrivez 0xFF.

La façon dont chaque type stocke ces valeurs est dans Variables et types.

La règle des parenthèses#

Un appel de commande porte toujours des parenthèses, même sans argument.

<Command>([<argument> [, <argument> ...]])
elapsed# = Timer()
width = DeviceWidth()
Sync()

Les formes nues d'AGK, de DarkBASIC et de PureBasic sont refusées :

PRINT "hello"
Sync
x = DeviceWidth
BASIC commands require parentheses: Print(...)
BASIC commands require parentheses: Sync(...)
BASIC commands require parentheses: DeviceWidth(...)

C'est ce qui permet au compilateur de distinguer une commande d'une variable. Un nom que le programme affecte (left = 10) est une variable ; un nom suivi de ( est un appel.

⚠️ Une variable utilisée seulement par GLOBAL, LOCAL ou CATCH, et jamais affectée par un simple nom = …, est encore lue comme la commande du même nom. LOCAL left = 3 dans une fonction est refusé avec BASIC commands require parentheses: Left(...). Choisissez un autre nom.

Les deux garde-fous#

Deux contrôles passent sur chaque ligne.

Commande inconnue#

Un appel dont le nom n'est ni une commande du catalogue, ni une fonction intégrée du compilateur, ni une FUNCTION utilisateur, ni un tableau, une liste ou un dictionnaire est refusé. Quand une commande du catalogue s'écrit presque pareil, le compilateur la nomme.

Prnt("hello")
Unknown BASIC command: Prnt() — did you mean Print()?
Blorp(1, 2)
Unknown BASIC command: Blorp()

La suggestion vient quand le nom diffère d'une lettre au plus (noms courts, jusqu'à quatre lettres) ou de deux lettres (noms plus longs).

Pas une instruction#

Une instruction est une affectation, un appel, ou commence par un mot-clé. Tout le reste est refusé, et le message cite le début de la ligne.

go to the shop
VAR n = 5
Not a BASIC statement: go to the shop
Not a BASIC statement: VAR n = 5

Ce que les garde-fous ne voient pas#

  • Un appel après un point (obj.method(...)) n'est pas comparé au catalogue. C'est ce qui garde les objets JavaScript accessibles.
  • Une expression inachevée, comme x = 1 + ou x =, passe les deux contrôles. Le programme ne démarre pas ensuite.
  • Un nom lu mais jamais affecté compile, et arrête le programme quand la ligne s'exécute (… is not defined). Voir Variables et types.
CommandeCe qu'elle fait
Print(...)dessine du texte sur l'écran du jeu ; un jeu l'appelle à chaque image
Console(...)écrit une ligne dans la console de l'éditeur
Debug(...)la même chose que Console, sous le nom de PureBasic
ConsoleWarning(...), ConsoleError(...)une ligne d'avertissement ou d'erreur dans la console
ClearConsole()vide la console

Pour vérifier une valeur pendant que vous travaillez, utilisez Console. Tous les exemples de ce manuel le font.

Un programme minimal#

En ligne droite : tourne une fois#

Sans boucle d'image, le programme tourne une fois de haut en bas, puis s'arrête. C'est la bonne forme pour un test, un outil, ou tout ce qui écrit dans la console.

name$ = "aukimi"
Console("Bonjour, " + name$)   ' Bonjour, aukimi

Avec une boucle d'image : tourne à chaque image#

Un jeu place sa préparation au-dessus de DO, et le corps de l'image entre DO et LOOP. Sync() termine chaque image.

frames = 0
DO
  frames = frames + 1
  Print("Image " + Str(frames))
  Sync()
LOOP

Une boucle sans Sync() ne laisserait jamais l'écran se dessiner, elle est donc refusée :

DO
  x = x + 1
LOOP
DO/LOOP requires SYNC() so the loop can yield a frame

⚠️ Dans un programme avec DO … LOOP, une FUNCTION ne voit pas les variables ordinaires posées au-dessus de DO. Déclarez les valeurs partagées avec GLOBAL. La raison, et la solution, sont dans Variables et types.

Il y a une seule boucle DO … LOOP de premier niveau par programme. Tout le reste sur les images, Wait, Sleep et les tâches est dans Boucle de jeu et tâches.

Vous venez d'un autre BASIC#

Vous écrivezDans aukimi
PRINT "x", Sync, Debug "x"Print("x"), Sync(), Debug("x")
PRINT pour voir une valeurConsole(...) : Print dessine sur l'écran du jeu
10 GOTO 20pas de numéros de ligne ; utilisez des étiquettes
_ pour continuer une ligneune instruction par ligne
"" dans une chaîne\" ou Chr(34)
&HFF, $FF0xFF
piPI
; commentaire' commentaire