Brass pour les habitués de Lua et ComputerCraft
Toutes les différences avec Lua 5.x et avec les API de ComputerCraft, leur raison d'être, et ce qu'il faut écrire à la place.
Si vous connaissez Lua, ou avez programmé des ordinateurs ComputerCraft, vous connaissez déjà l'essentiel de Brass : les mêmes mots-clés, les mêmes tables, local, pairs, .., string.format. Cette page liste tout ce qui diffère, pour que vos habitudes travaillent pour vous et non contre vous. Un tableau comparatif clôt la page.
La plupart des différences ont l'une de deux raisons. Brass tourne sur sa propre petite machine virtuelle à l'intérieur du serveur, qui compte chaque instruction et chaque cellule de mémoire de chaque ordinateur : aucun programme ne peut faire laguer le serveur ni dévorer sa mémoire. Et Brass est fait pour s'apprendre en jeu : quelques pièges de Lua ont été retirés ou changés en erreurs claires.
Ce qui ne change pas
- La syntaxe :
local,function,if ... elseif ... else ... end,while,repeat ... until, lefornumérique,for k, v in pairs(t),break,do ... end,return. - Les commentaires
--et--[[ ... ]], les chaînes entre"...",'...'et[[ ... ]]. - Les tables indexées à partir de 1,
#t,t.nomett[cle],{1, 2, x = 3}. and,or,not, avec la même règle « seulsniletfalsesont faux » (donc0et""sont vrais).//(division entière) et%comme en Lua 5.3,^pour les puissances.- Les appels de méthode
obj:methode(x)etfunction obj:methode(x)avecself, les raccourcis d'appelf "texte"etf {table}. tostring,tonumber,type,pairs,ipairs,error,assert, et les fonctions habituelles destring,tableetmath.
Le langage
Pas de fermetures sur les locales d'une fonction
C'est la différence que vous rencontrerez en premier. Une fonction écrite à l'intérieur d'une autre fonction ne peut pas utiliser les variables locales ni les paramètres de celle-ci. Le grand classique de Lua ne compile pas :
local function creer_compteur()
local n = 0
return function()
n = n + 1
return n
end
endsnippet:4: cannot capture local 'n' of an enclosing function
Ce qu'une fonction peut utiliser :
- ses propres paramètres et locales ;
- les variables globales ;
- les locales du niveau du fichier (déclarées hors de toute fonction) : elles se comportent comme des variables du module, partagées par toutes les fonctions du fichier ;
- elle-même, quand elle est déclarée avec
local function f(la récursivité marche).
L'état va donc dans une locale du niveau du fichier, dans une table passée en paramètre, ou dans une table utilisée comme objet :
local function nouveau_compteur()
return {n = 0}
end
local function avancer(compteur)
compteur.n = compteur.n + 1
return compteur.n
end
local c = nouveau_compteur()
avancer(c)
print(avancer(c))2
La variable d'une boucle for écrite au niveau du fichier est une seule variable du fichier, pas une copie neuve à chaque tour : une fonction créée dans la boucle voit sa valeur du moment où elle est appelée, pas celle de son tour.
local actions = {}
for i = 1, 3 do
actions[i] = function() return i end
end
print(actions[1](), actions[3]())3 3
Rangez la valeur dans une table (actions[i] = {etape = i}) quand chacune a besoin de la sienne.
Une fonction renvoie une seule valeur
return a, b est une erreur de compilation : a function returns a single value (return a table instead). Renvoyez une table :
local function en_piles(nombre)
return {piles = nombre // 64, reste = nombre % 64}
end
local p = en_piles(1234)
print(p.piles, p.reste)19 18
local a, b = f() compile, mais b vaut toujours nil. L'affectation multiple elle-même marche : a, b = b, a échange deux valeurs.
La bibliothèque suit la même règle. Là où Lua renvoie plusieurs valeurs, Brass en renvoie une, ou une table :
| Lua | Brass |
|---|---|
local i, j = s:find("x") | local i = s:find("x"), la fin est i + #"x" - 1 |
local ok, err = pcall(f) | local r = pcall(f), puis r.ok, r.value, r.error |
local x, y = term.getCursorPos() | local c = term.get_cursor(), puis c.x, c.y |
local w, h = term.getSize() | local t = term.get_size(), puis t.w, t.h |
local event, a, b = os.pullEvent() | local e = os.pull_event(), puis e.name et des champs nommés |
Pas de ..., ni select, ni unpack
function f(...) est une erreur de compilation (variable arguments ('...') are not supported), et select, unpack et table.unpack n'existent pas. Donnez à une fonction une table au lieu d'un nombre variable d'arguments : somme({3, 4, 5}). Les fonctions de bibliothèque acceptent toujours plusieurs arguments quand c'est utile : print, write, math.min, math.max, string.format, string.char.
Les arguments d'un programme tapés à l'invite (run stock nord 64, ou stock nord 64) sont dans la table globale arg : arg[1] vaut "nord", arg[2] vaut "64" (du texte), et arg[0] est le chemin du programme.
Pas de métatables
setmetatable, getmetatable, rawget, rawset, rawequal, rawlen et tous les __index, __call, __add... sont absents. Un objet est une simple table qui contient ses données et ses fonctions ; obj:methode() passe la table en self. Pour donner les mêmes fonctions à plusieurs objets, copiez-les dedans, avec table.copy par exemple :
local Machine = {}
function Machine.decrire(self)
return self.nom .. " à " .. self.vitesse .. " tr/min"
end
local function nouvelle_machine(nom, vitesse)
local m = table.copy(Machine)
m.nom = nom
m.vitesse = vitesse
return m
end
local presse = nouvelle_machine("Presse mécanique", 64)
print(presse:decrire())Presse mécanique à 64 tr/min
Programmes en plusieurs fichiers montre la même idée sous forme d'un module qui construit des objets.
Pas de coroutines
Il n'y a pas de bibliothèque coroutine, ni de parallel de ComputerCraft. Un programme Brass suit un seul fil d'exécution. Pour faire plusieurs choses à la fois (faire clignoter une lampe, surveiller un levier, répondre au clavier), écrivez une seule boucle d'évènements qui traite chaque sorte d'évènement, avec des minuteurs pour les tâches périodiques :
local clignotement = os.start_timer(0.5)
local allumee = false
while true do
local e = os.pull_event()
if e.name == "timer" and e.id == clignotement then
allumee = not allumee
rs.set("top", allumee)
clignotement = os.start_timer(0.5)
elseif e.name == "redstone" then
print("levier : " .. rs.get("left"))
elseif e.name == "key" and e.key == "enter" then
print("Entrée pressée")
end
endÉvènements construit ce modèle pas à pas.
Pas de goto
goto et les étiquettes (::continue::) n'existent pas : goto continue s'arrête sur syntax error near 'continue'. Pour sauter la fin d'un tour, mettez-la dans un if :
for _, objet in ipairs({"fer", "", "or"}) do
if objet ~= "" then
print(objet)
end
endfer or
Les chaînes
- Pas de motifs.
string.findcherche le texte exactement tel qu'il est écrit (son quatrième argument est ignoré) et ne renvoie que la position de début.string.match,string.gmatchetstring.gsubn'existent pas.Travailler avec du textemontre comment faire chaque tâche courante sans eux. string.formatconnaît%d %i %f %s %x %X %%, avec les drapeaux- 0 + espace, une largeur et une précision d'au plus deux chiffres.%q,%e,%g,%c,%oet%asont refusés.- Des caractères, pas des octets.
#"café"vaut 4 (5 en Lua),string.bytedonne des codes de caractère jusqu'à 65535 ("€"vaut 8364),string.charles accepte, etupper/lowergèrent les accents ("été"donne"ÉTÉ"). - Pas de conversion automatique dans les calculs :
"10" + 1est une erreur, pas 11.tonumberconvertit...change toujours les nombres en texte. - Séquences d'échappement : seulement
\n \t \r \\ \" \' \0 \xNN(pas de\ddd,\u{...},\z,\a...). Les chaînes longues n'ont qu'un niveau,[[ ... ]](pas de[==[). - Extensions :
string.split,string.trim,string.starts,string.ends. - Une chaîne contient au plus 65536 caractères.
Les nombres
- Une seule sorte de nombre, un flottant sur 64 bits. Il n'y a pas de sous-type entier :
10 / 2affiche5(Lua 5.3 affiche5.0), etmath.type,math.tointeger,math.maxintegeretmath.modfn'existent pas. Les nombres entiers sont exacts jusqu'à 2^53. - Les nombres s'affichent comme le
%.14gde Lua :0.1 + 0.2affiche0.3. - Pas d'opérateurs bit à bit :
&,|,~,<<,>>sont des erreurs de compilation (unexpected symbol '&'), et il n'y a pas debit32. Passez par l'arithmétique : le bitndexvautx // 2 ^ n % 2. - Extension :
math.round.
Les tables et les boucles
for v in liste doparcourt les valeurs d'une liste (une extension de Brass).for k, v in tsanspairsest une erreur qui vous le dit.pairsparcourt la partie liste dans l'ordre, puis les autres clés dans l'ordre où elles ont été ajoutées : le même ordre à chaque lancement, contrairement à Lua. Ajouter ou retirer des clés pendant la boucle ne pose pas de problème.table.sortest stable (les éléments égaux gardent leur ordre).table.remove(t)sur une table vide renvoienil(table.remove(t, 1)est une erreur).- Extensions :
table.copy(une copie de surface),table.contains,table.keys. - Absents :
table.unpack,table.pack,table.move, etnext. Pour savoir si une table est vide, utilisez#table.keys(t) == 0. !=marche comme~=.
Les erreurs
pcallrenvoie une table :{ok = true, value = ...}ou{ok = false, error = "..."}. Méfiez-vous de l'habitude de Lua, qui compile mais ne fait pas ce que vous croyez :
local ok, err = pcall(error, "boum")
print(type(ok), err)
local r = pcall(error, "boum")
print(r.ok, r.error)table nil false snippet:3: boum
error(message)ajoute toujours la positionfichier:ligne:de l'appel ; son second argument (le niveau) est ignoré. Une valeur qui n'est pas une chaîne devient du texte : on ne peut donc pas lever une table pour transporter des données.- Pas de
xpcallni de trace d'appels : un message nomme la ligne où l'erreur s'est produite, pas les appels qui y ont mené. - Ctrl+T ne se rattrape pas : il n'y a pas d'évènement
terminateni d'os.pullEventRaw.
Erreurs et débogage traite tout cela en détail.
Les globales absentes
load, loadstring, dofile, loadfile, next, select, unpack, rawget et compagnie, setmetatable, getmetatable, collectgarbage, xpcall, _G, _ENV et _VERSION n'existent pas, pas plus que les bibliothèques coroutine, io, debug, utf8, package et bit32. Dans os, seules les fonctions de os existent (pas d'os.date, os.exit, os.getenv, os.remove...). Appeler une fonction absente s'arrête sur attempt to call a nil value (global 'load'). Pour exécuter un autre fichier, utilisez import().
Les API, comparées à ComputerCraft
Des noms en snake_case
Tous les noms de Brass sont en minuscules avec des tirets bas : os.pull_event, os.start_timer, os.queue_event, os.day_time, term.set_cursor, term.get_size, fs.make_dir, fs.is_dir. Un nom de ComputerCraft s'arrête sur attempt to call a nil value (field 'pullEvent').
Les évènements sont des tables
os.pull_event([nom]) renvoie une table, avec le nom dans e.name et les données dans des champs nommés :
-- ComputerCraft : local event, key = os.pullEvent("key")
local e = os.pull_event("key")
if e.key == "enter" then
print("confirmé")
end| évènement | champs |
|---|---|
timer | id |
key | key : un nom ("enter", "backspace", "delete", "up", "down", "left", "right", "home", "end", "tab") |
char | char : le caractère tapé (lettres, chiffres et espace arrivent ici, pas en key) |
paste | text |
click, drag | x, y (caractère), px, py (pixel), button (1, 2, 3), source ("terminal" ou "monitor") |
redstone | aucun : lisez les côtés avec rs.get |
message | sender, channel, data, via, distance |
disk | inserted (true ou false) |
Les touches sont des noms, pas des nombres : il n'y a pas de table keys, ni d'indicateur « touche maintenue ». os.queue_event(nom, donnee) transporte une seule valeur, dans e.data. Comme dans ComputerCraft, attendre un nom précis jette les autres évènements, et la file contient au plus 256 évènements. La liste complète est dans Évènements.
La redstone : rs
Il n'y a pas de table redstone, seulement rs, et elle parle en niveaux :
| ComputerCraft | Brass |
|---|---|
rs.setOutput("top", true) | rs.set("top", true) ou rs.set("top", 15) |
rs.setAnalogOutput("top", 7) | rs.set("top", 7) |
rs.getInput("left") | rs.get("left") > 0 |
rs.getAnalogInput("left") | rs.get("left") |
rs.getSides() | rs.sides() |
rs.get renvoie un nombre, et 0 est vrai dans une condition : if rs.get("left") then est toujours pris. Comparez avec > 0. Les côtés sont front, back, left, right, top, bottom, gauche et droite vus en faisant face à l'écran. Voir rs.
Les périphériques
peripheral.wrap(nom) renvoie une table de fonctions (ou nil quand il n'y a pas d'appareil). Appelez-les avec un point, comme les périphériques de ComputerCraft :
local feu = peripheral.wrap("left")
feu.set("green")Les deux-points (feu:set("green")) passeraient la table elle-même en premier argument, et l'appel échouerait sur bad argument #1 to 'set' (string expected, got table). (Les deux-points sont pour vos propres objets, voir plus haut.)
| ComputerCraft | Brass |
|---|---|
peripheral.getNames() | peripheral.list(), ou peripheral.list("inventory") pour un seul type |
peripheral.getType(nom) | peripheral.type(nom) |
peripheral.find(type) : toutes les correspondances | peripheral.find(type) : la première seulement ; enveloppez les noms de peripheral.list(type) pour toutes |
peripheral.call(nom, methode, ...) | pareil |
peripheral.getMethods(nom) | la table enveloppée est une simple table : table.keys(appareil) |
des noms réseau comme "monitor_0" | les faces ("left") ou "type@x,y,z" pour un bloc sur un Câble de données |
Les appareils et leurs méthodes sont propres à Brass (machines de Create, coffres à objet renforcés, Feux tricolores, capteurs) : voir Tous les périphériques. On n'écrit pas sur un moniteur par peripheral : un moniteur qui touche l'ordinateur affiche son écran, donc print, term et gfx dessinent directement dessus (l'envelopper donne seulement sa taille, @monitor.size).
Le terminal et les couleurs
term.setTextColoretterm.setBackgroundColorsontterm.set_fgetterm.set_bg;term.setCursorPosestterm.set_cursor;term.get_cursoretterm.get_size(lesgetCursorPosetgetSizede ComputerCraft) renvoient des tables,{x = ..., y = ...}et{w = ..., h = ...}.- Les couleurs sont des nombres de 0 à 15, pas les puissances de deux de ComputerCraft, et elles vivent dans
term.colors(il n'y a pas decolorsni decoloursglobal) :
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| white | orange | magenta | light_blue | yellow | lime | pink | gray | light_gray | cyan | purple | blue | brown | green | red | black |
Une valeur de ComputerCraft comme colors.red (16384) s'arrête sur bad argument #1 to 'set_fg' (color must be 0..15). Le nombre de Brass est l'exposant de la puissance de deux de ComputerCraft : 16384 vaut 2^14, donc le rouge est 14. Les fonctions de gfx acceptent aussi les noms en texte : gfx.rect(1, 1, 20, 10, "red").
- Il n'y a ni
term.blit, niterm.redirect, niterm.isColor, niwindow, nipaintutils. Le dessin passe pargfx(pixels, lignes, formes, petit texte). Les écrans des premiers âges (papier, phosphore vert et ambre) dessinent tout dans leur unique encre.
Les fichiers
Il n'y a ni descripteurs de fichier (handles) ni io. Un fichier se lit et s'écrit en un seul appel :
| ComputerCraft | Brass |
|---|---|
fs.open(p, "r") puis h.readAll(), h.close() | fs.read(p) : tout le texte, ou nil |
lire ligne à ligne avec h.readLine() | fs.read(p):split("\n") |
fs.open(p, "w") puis h.write(t), h.close() | fs.write(p, t) (crée les dossiers en chemin) |
fs.open(p, "a") | fs.append(p, t) |
fs.makeDir, fs.isDir, fs.getSize | fs.make_dir, fs.is_dir, fs.size |
fs.getFreeSpace, fs.getCapacity | fs.free(), fs.capacity() |
fs.combine(a, b) | a .. "/" .. b |
Les chemins partent du dossier courant (fs.cwd()), ou de la racine avec un / au début. Les noms utilisent des lettres, des chiffres, _, . et -. Voir fs.
Le réseau
net remplace rednet, sans rednet.open : le Câble de données, le Modem radio ou le Routeur Wi-Fi se charge de la connexion.
| ComputerCraft | Brass |
|---|---|
rednet.open("back") | rien à faire |
rednet.send(id, msg, protocole) | net.send(id, donnee, canal) : true si livré |
rednet.broadcast(msg, protocole) | net.broadcast(donnee, canal) : le nombre d'ordinateurs atteints |
local id, msg = rednet.receive(protocole, 5) | local m = net.receive(5) : m.sender, m.data, m.channel, ou nil après 5 s |
os.getComputerID() | os.id() ou net.id() |
net.receive ne prend pas de canal : testez m.channel vous-même. Les messages sont des copies de données simples (nombres, texte, booléens, tables de ces valeurs), jusqu'à 32768 caractères. net existe à partir du Mini-ordinateur. Voir net et Réseaux.
Les programmes et le shell
- L'interpréteur interactif est
brass, paslua. - Le fichier de démarrage s'appelle exactement
startup(passtartup.lua, pas de dossierstartup). - Les arguments d'un programme arrivent dans la globale
arg, pas dans.... - Il n'y a pas d'API
shell(pas deshell.run) ni demultishell: un seul programme tourne à la fois. Un programme en lance un autre avecimport, qui charge aussi les bibliothèques avec des chemins relatifs au fichier (import "lib/texte"), voirProgrammes en plusieurs fichiers.requireest la même fonction. - Pas de
textutils(netenvoie les tables telles quelles ; pour ranger une table dans un fichier, écrivez ses champs en texte), pas desettings(utilisez un fichier de réglages, voirTravailler avec du texte). os.getComputerLabeletos.setComputerLabelsontos.label()etos.label(texte).readprend un seul argument facultatif, un caractère de masque pour les mots de passe :read("*"). Pas d'historique ni de complétion en arguments.- Pas de
http,disk,gps,turtlenipocket. À leur place, des bibliothèques pour Create :link(les Liaisons de redstone),display(les Liaisons d'affichage),vehicle(Create Aeronautics).
Le temps
sleep(secondes)attendceil(secondes × 20)ticks, au moins un :sleep(0)attend un tick. Il n'y a pas d'os.sleep.- **
os.time()n'est pas l'heure du jour** : il compte les ticks depuis le démarrage de l'ordinateur. L'heure du jour estos.day_time(), de 0 à 23999 (6000 est midi).os.clock()donne les secondes depuis le démarrage. Il n'y a nios.epoch, nios.day, nios.date. os.start_timer(secondes)renvoie un numéro et met plus tard{name = "timer", id = ...}dans la file. Il n'y a pas d'os.cancelTimer: gardez le numéro du minuteur qui vous intéresse et ignorez les autres. Pas d'os.setAlarmnon plus : comparezos.day_time()dans une boucle. 256 minuteurs peuvent attendre en même temps.
Temps et minuteries donne les détails.
La vitesse : un budget d'instructions, jamais « too long without yielding »
ComputerCraft tue un programme qui calcule quelques secondes sans rendre la main (Too long without yielding), si bien que les longues boucles ont besoin d'astuces comme sleep(0) ou os.queueEvent. Brass, jamais. Chaque ordinateur exécute un nombre fixe d'instructions par tick ; quand elles sont épuisées, le programme se met en pause là où il est et repart au tick suivant. Un while true do end est sans danger pour le serveur : il n'use que le temps de son propre ordinateur.
| ordinateur | instructions par seconde à 256 tr/min |
|---|---|
| Calculateur à tubes | 400 |
| Ordinateur à transistors | 1 600 |
| Mini-ordinateur | 6 000 |
| Micro-ordinateur | 24 000 |
| Microcontrôleur | 8 000 |
| Ordinateur moderne | 100 000 |
Le budget suit la rotation de l'arbre qui entraîne l'ordinateur : moitié moins vite à 128 tr/min, rien sans rotation. Les gros travaux de la bibliothèque coûtent des instructions en plus (trier, les longs textes, parcourir un réseau). Deux conséquences : une boucle qui vérifie quelque chose sans arrêt (attente active) gaspille le temps dont le reste du programme a besoin, alors attendez avec os.pull_event ou sleep ; et sur les plus vieux ordinateurs, moins d'instructions veut dire des programmes plus simples. Voir Vitesse, mémoire et limites.
La mémoire en cellules
Un programme ComputerCraft peut utiliser autant de mémoire que le Java du serveur lui en donne. Un ordinateur Brass a une mémoire fixe, comptée en cellules :
| ordinateur | mémoire |
|---|---|
| Calculateur à tubes | 2 048 cellules |
| Ordinateur à transistors | 8 192 cellules |
| Mini-ordinateur | 32 768 cellules |
| Micro-ordinateur | 131 072 cellules |
| Microcontrôleur | 16 384 cellules |
| Ordinateur moderne | 1 048 576 cellules |
Une table coûte 4 cellules plus 2 par entrée, une chaîne 1 cellule plus 1 pour 8 caractères, un appel de fonction 8 cellules tant qu'il dure. Dépasser la capacité arrête le programme sur out of memory. os.memory() renvoie {used = ..., total = ...}, et la commande mem l'affiche. Autres limites fixes : 200 appels imbriqués (stack overflow), 65536 caractères par chaîne, 256 évènements en file, 256 minuteurs en attente. Voir Limites.
Côte à côte
| Dans ComputerCraft | En Brass |
|---|---|
local ok, err = pcall(f, x) | local r = pcall(f, x), puis r.ok, r.value, r.error |
return a, b | return {a = a, b = b} |
function f(...) | function f(liste) |
local args = {...} (arguments du programme) | arg[1], arg[2]... |
| des fermetures sur les locales d'une fonction | locales du fichier, paramètres, tables avec self |
setmetatable(obj, {__index = Classe}) | copier les fonctions dedans : table.copy(Classe) |
coroutine, parallel.waitForAny | une boucle d'évènements avec os.pull_event et des minuteurs |
s:gsub("a", "b") | table.concat(s:split("a"), "b") |
s:match("^%s*(.-)%s*$") | s:trim() |
bit32.band(a, b), a & b | de l'arithmétique avec // et % |
os.pullEvent("key") | os.pull_event("key"), qui renvoie une table |
keys.enter | "enter" (dans e.key) |
os.startTimer(1) | os.start_timer(1) |
os.cancelTimer(id) | ignorer l'évènement du minuteur |
os.time() (l'heure du jour) | os.day_time() (de 0 à 23999) |
os.epoch("utc") | pas d'heure réelle : os.time() compte les ticks depuis le démarrage |
os.getComputerID() | os.id() |
os.setComputerLabel("x") | os.label("x") |
sleep(0.05) / os.sleep | sleep(0.05) (un tick) |
redstone.setOutput("top", true) | rs.set("top", true) |
redstone.getAnalogInput("left") | rs.get("left") |
peripheral.find("monitor") | un moniteur affiche l'écran de l'ordinateur : print, simplement |
peripheral.getNames() | peripheral.list() |
term.setTextColor(colors.red) | term.set_fg(term.colors.red) (14) |
colors.white = 1, colors.black = 32768 | term.colors.white = 0, term.colors.black = 15 |
term.setCursorPos(x, y) | term.set_cursor(x, y) |
local x, y = term.getCursorPos() | local c = term.get_cursor() |
local w, h = term.getSize() | local t = term.get_size() |
paintutils.drawFilledBox(...) | gfx.rect(x, y, l, h, couleur, true) |
fs.open(p, "r").readAll() | fs.read(p) |
fs.open(p, "w") + write + close | fs.write(p, texte) |
fs.makeDir(p) | fs.make_dir(p) |
rednet.open("back") | rien |
rednet.send(id, msg, "proto") | net.send(id, msg, "proto") |
rednet.receive(nil, 5) | net.receive(5), qui renvoie une table ou nil |
textutils.serialize(t) | pas d'équivalent : net envoie les tables telles quelles |
shell.run("prog") | import "prog" |
lua (l'invite interactive) | brass |
startup.lua | startup |
| « Too long without yielding » | jamais : le budget met le programme en pause puis le reprend |
| mémoire illimitée | de 2 K à 1 M cellules, out of memory |
Voir aussi
Valeurs et variables,Fonctions,Tables: le langage depuis le début.Erreurs et débogage: les messages d'erreur, etpcallen pratique.Aide-mémoire: tout le langage et ses bibliothèques sur une page.