Brainf*ck interpreter

I found in my archives a brainf*ck interpreter written five years ago.

Brainf*ck is a 8 (eight) instructions set programming language:

  • < and > walks through a set of pointers (0 -> ...)
  • + and - increases or decreases the current pointer (0 <->255)
  • [ jumps to the related ']' if the current pointer is 0
  • ] jumps back to the related '[' if the current pointer is > 0
  • . displays the character equivalent to the ascii rank defined by the current pointer (65 is 'A')
  • , gets a character and puts it in the current pointer (in this app: waits a key with raw_input!)

This application is on debugging mode: it vertically displays any change of pointer, value and output!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#! /usr/bin/python
 
# Brainf*ck interpreter
# Jean-Christophe Beumier - GPL2 license
# Version 1.00 2009.07.28 -
 
i=0; ptr=0; bytes=[0]; depth=0
 
prg="Capitalizes a minuscule: >>,<<++++[>++[>----<-]<-]>>." # here comes the bf script
end=len(prg)
 
while (i<end):
  char=prg[i] # spells the program string
 
  if (char=='>'):
    ptr=ptr+1
    if ptr>len(bytes)-1: # needs to add a cell to the pointer list
      bytes+=[0]
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr], "pter +1")
 
  elif (char=='<'):
    ptr=ptr-1;
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr], "pter -1")
    if (ptr<0):
      print "*** Negative pointer doesn't work ***"
 
  elif (char=='+'):
    bytes[ptr]=bytes[ptr]+1;
    if bytes[ptr]==256: bytes[ptr]=0
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"byte +1")
 
  elif (char=='-'):
    bytes[ptr]=bytes[ptr]-1 ;
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"byte -1")
    if bytes[ptr]==-1: bytes[ptr]=255
 
  elif (char=="["):
    if (bytes[ptr]==0):
      print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"cancelled loop!")
      depth=1
      while(depth>0):
        i+=1
        if prg[i] =="[":
          depth+=1
        if prg[i] =="]":
          depth-=1
    else:
      print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"loop in")
 
  elif (char=="]"):
    if (bytes[ptr]>0):
      print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"loop on")
      depth=1
      while(depth>0):
        i=i-1
        if prg[i] =="]":
          depth+=1
        if prg[i] =="[":
          depth-=1
    else:
      print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"loop out ")
 
  elif (char=='.'):
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"output: "), chr(bytes[ptr])
 
  elif (char==','):
    print "% 6.f#% 7.f% 4.f  %s" %(i, ptr, bytes[ptr],"input:  "),
    key=raw_input()
    bytes[ptr]=ord(key)
 
  else:
    print "% 6.f#% 7.f% 4.f  Comment: %s" %(i, ptr, bytes[ptr], char)
  i=i+1
 
raw_input("\n###  End of program. Strike [Enter]  ###")

You can try the canonical Hello world!

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.

 

 

displaying dynamic graphic (and saving it after a while)

A friend of mine, who plays with a raspberry nano computer, wanted to display a sensor (don't remember which). So I tried to write a program getting a data, displaying it by Tkinter, and saving the graphic.

Un ami, qui s'amuse avec le nano computer raspberry, désirait afficher les données d'une sonde (je ne sais plus laquelle). J'ai donc tenté d'écrire un programme qui reçoit des données, les affiche en continu et les sauve de temps en temps.

This script does:

  • display a dot every second (using the Tkinter after() fonction) at the right of the graphic
  • shift the graphic one pixel per second to the left, letting you see the ten past minutes
  • tag the time (hh:mm) at the 50th second, with a matrixed digit method
  • save the graphic every ten minutes (.PGM, Portable Gray Map, a category of PNM)
  • seem to be python3 compatible (displaying: yes, saving: smog until now)
  • what else?

Ce script:

  • inscrit un point toutes les secondes à la droite du graphique, avec la méthode after() de Tkinter
  • déplace le graphique d'un pixel à droite, permettant de regarder les dix dernières minutes
  • écrit le temps hh:mm à la 50e secondes, avec une routine basée sur une matrice de points
  • sauve le graphique toutes les dix minutes (PGM, Portable Gray Map, une catégorie de PNM)
  • semble être compatible avec python3 (l'affichage, mais la sauvegarde est une purée de pois)
  • quoi d'autre?

The script is more or less commented, but I'm a rather wild scripter.

Calling a routine every second - after(1000, routine) - is not the best way to pace a program, because there are so many things the program does between each call! I prefer to ask every tenth of second whether it's already the next second. You can do better with a call every hundredth of second.

Le script est plus ou moins commenté (en mauvais anglais).

Utiliser une pause d'une seconde par la méthode after(1000, routine) (Tkinter) est une mauvaise chose, parce que le script prend également du temps pour faire tout ce qu'il a à faire entre deux pauses. Je préfère donc demander tous les dixièmes de seconde si le système est passé à la seconde suivante. Vous pouvez faire mieux en faisant un appel tous les 100e de secondes. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#! /usr/bin/python -Qnew
 
# 2014.11.14 Jean-Christophe Beumier - www.jchr.be
# GPL2, see www.gnu.org/licenses/licenses.html
 
# dynamic display of data from any sensor or computing
# saving graphic files .PNM every 10 minutes
 
import time, os
from Tkinter import *
from math import *
 
# for python 3:
#! /usr/bin/python3
# from tkinter import * 
 
# digit matrix for printdate()
dates=[[6, 9, 9, 9, 9, 9, 6]]
dates+=[[2, 6, 2, 2, 2, 2, 7]]
dates+=[[6, 9, 1, 2, 4, 8, 15]]
dates+=[[6, 9, 1, 2, 1, 9, 6]]
dates+=[[2, 2, 6, 6, 10, 15, 2]]
dates+=[[15, 8, 14, 1, 1, 9, 6]]
dates+=[[6, 8, 14, 9, 9, 9, 6]]
dates+=[[15, 1, 2, 2, 4, 4, 4]]
dates+=[[6, 9, 9, 6, 9, 9, 6]]
dates+=[[6, 9, 9, 9, 7, 1, 6]]
 
tab=[chr(255)]*66000   # list ("table") for image 600x100px
for i in range(600):
  tab[29400+i]=chr(144) # gray horizontal line at mid-height
imgdata="".join(tab) # joining the bytes
p5="P5 600 110 255\n"+imgdata # header and image-data 
 
ang=0
def sensor():
  global ang
  #   on UNIX, 'acpi -t' returns 'Thermal 0: ok, 36.0 degrees C'
  # pfile=os.popen("acpi -t", "r")
  # line=pfile.read()
  # pfile.close()
  # temp=int(round(float(line.split()[3]))) # 4th string of the line
 
  #   not sure you're on Unix, so let's compute something:
  calcul=sin(ang/19)/(1+abs(tan(ang/27)))*39+abs(cos(ang/10))*10
  ang+=1
  return(calcul) 
 
def printdate(x):
  global dates, tab
  if x==1:
    string=time.strftime("%H:%M")
    colour=0 # black digits for time
  else:
    string=time.strftime("%Y.%m.%d")
    colour=80 # dark gray digits for date
  off=0
  for p in string:
    try:
      a=int(p)  # is a number
      cp=0 # vertical offset for digit matrix printing
      for q in dates[a]: # q is a number matrix
        for r in range(4):
          if q & 2**r: # matching bit of number
            tab[600*(102+cp)+549+off+5-r]=chr(colour)
        cp+=1
      off+=5
    except:
      if p==".": # not a number
        tab[600*108+548+off+3]=chr(0)
        off+=3
      elif p==":":
        tab[600*104+549+off+3]=chr(0)
        tab[600*107+549+off+3]=chr(0)
        off+=4
 
h=49 # mid-height
count=int(time.strftime("%S")) # getting the first second
filename=time.strftime("%Y.%m.%d-%Hh%M")
def formage():
  global count, tab, p5, h, filename
  if count%10==0:  # gray dotted vertical line every 10px
    if count==0:   # the first darker
      for i in range(100):
        tab[i*600+599]=chr(64)
    elif count%60==0: # plain line every 60px (minute)
      for i in range(100):
        tab[i*600+599]=chr(144)
    else:
      for i in range(50): # printing date or time
        tab[i*1200+1199]=chr(144)
      if count%60==50:
        if count//60==1:
          printdate(2) # date
        else:
          printdate(1) # time
      
  sortie=sensor()
  h=50+int(sortie)
  if h>99: h=99 # display from 0 to 99, no outside
  if h<0: h=0 
  tab[(99-h)*600+599]=chr(0) # writing the dot into the matrix
 
  p5="P5 600 110 255\n"+"".join(tab) # assembling PNM image
  for i in range(110): # rubbing out the first column
    tab[i*600]=chr(255)
  tab=tab[1:]+[chr(255)] # deleting 1 byte: shifting 1px to left
  tab[29999]=chr(144)    # middle point
  if count%2==1: # 1 out of 2px
    tab[5999]=chr(144)   # adding 90%
    tab[11999]=chr(144)  # adding 80%
    tab[17999]=chr(144)  # adding 70%
    tab[23999]=chr(144)  # adding 60%
    tab[35999]=chr(144)  # adding 40%
    tab[41999]=chr(144)  # adding 30%
    tab[47999]=chr(144)  # adding 20%
    tab[53999]=chr(144)  # adding 10%
 
  count+=1
  if count==600: # saving after 600 dots (image width)
    han=open("tk-%s.pgm" %(filename),"w")
    han.write(p5)
    han.close()
    count=0
    filename=time.strftime("%Y.%m.%d-%Hh%M") # next one
 
  label0.config(text="%5.2f" %(round(sortie,2)))  # rewriting
  image0.config(data=p5)       # image redrawing
  label1.config(image=image0)  # image redisplaying
 
sec0=99
def checktime():
  global sec0
  nsec=int(time.strftime("%S"))
  if nsec!=sec0:
    sec0=nsec    
    formage()
  root0.after(100, checktime)  # waits 0.1sec, before reiteration
 
# tkinter setting:
 
root0=Tk()
root0.geometry("620x150")
label0=Label(root0, text="0.00")
label0.pack()
label0.after(0, checktime)   # waiting 0 sec and calling
image0=PhotoImage(data=p5)
label1=Label(image=image0) # image displaying
label1.image=image0
label1.pack()
 
root0.mainloop()

 

 

gui for 'boucles' application

2014.11.07: on debugging now - maintenant en débogage

Do you want to be able to produce this kind of image

 

...with this kind of application (python+Tkinter)?

Coming soon!

French help-info (I would like to have english, spanish and dutch (.nl) translation):

"""Translation by Your-Name - 2014.12.xx

Argument
Comme la terre tourne autour du soleil, la lune autour de la terre et Lunik 10 autour de la lune du 3 avril au 30 mai 1966, cette application dessine une courbe fermée composée de trois cycles dans un carré de 512px de côté

Boutons du haut

[Exit] fait quitter l'application
[Charg.] (en débogage) charge des paramètres en fonction du titre d'une image déjà sauvegardée.
[Sauv.] sauvegarde le graphique réalisé par l'application, au format PNM: «P5» pour les dessins en N/B ou gris, ou «P6» pour les dessins en couleurs. Pour les systèmes UNIX munis de ImageMagick, le fichier au format PNM est transformé en PNG, économique et sans perte de qualité. Les données sont écrites dans le nom du fichier, ce qui permet à l'application de se remémorer les paramètres en chargeant le fichier.

Informations

'00008 kilopt' donne le nombre de milliers de points que les boutons [Dessin] ou [Ajout] vont dessiner. Sur un amd64 à 1.7GHz, cela prend environ 20 kpt par seconde: Le maximum, 30720, prendrait alors 1500 secondes, c'est-à-dire 25 minutes!
[.fr], [.en], [.es] et [.nl] affichent la documentation en une de ces quatres langues.
[version] fournit les diverses étapes de l'écriture de l'application, le todo et les bugs connus.

Cycle primaire

'C1: 000px' est la longueur du cycle primaire, il est déduit de la longueur des cycles secondaire et tertiaire, la somme des trois cycles valant 250px
'C2: 000px' (seulement en mode «Rafale») est la longueur du cycle primaire pour une seconde courbe.
'Trigo' rappelle que le premier cycle court toujours dans le sens trigonométrique.
'+000°' permet de réorienter la courbe (de 0 à 360°).
*Cycles secondaire et tertiaire
'C1: 000px' affiche la longueur du rayon du cycle secondaire ou tertiaire (de 0 à 120px), modifiable par l'ascenseur horizontal situé à droite.
'C2: 000px' (seulement en mode «Rafale») idem pour une deuxième courbe.
'000sp' afffiche le nombre de spires du cycle secondaire ou tertiaire, modifiable par l'ascenseur à droite (de 0 à 100).
[Trigo] définit le sens du cycle secondaire ou tertiaire: noir sur fond blanc pour le sens trigonométrique, noir sur fond gris pour le sens horaire.
'+000°' permet de déphaser les cycles secondaire ou tertiaire (de 0 à 360°).

Généralités

[ ] appelle la boîte de sélection des couleurs pour définir la couleur de fond de l'image.
[1] appelle la boîte de sélection des couleurs pour définir la couleur de la première courbe.
[<>] (en mode «rafale» seulement) permet d'intervertir les couleurs de la première et seconde courbe.
[2] (en mode «rafale» seulement) appelle la boîte de sélection des couleurs pour définir la couleur de la seconde courbe.
[g] définit l'épaisseur de la courbe: noir sur fond blanc pour deux pixels, noir sur fond gris pour un simple pixel.
'd: 4 ^' permet de définir la densité de la courbe, de 1 à 16 milliers de points pour la révolution complète d'une courbe (4 milliers par défaut).
*Répétitions
'x 1 ^' permet de répéter le graphique: pour n répétitions, la courbe est répétée tous les (360/n)°.
[Rafale] permet de passer en mode «rafale», à savoir plusieurs courbes de la première à la seconde (comprise), définies selon les rayons '1' et '2' et les couleurs associées. Les courbes intermédiaires sont colorées selon le gradient de couleur.
'05' (mode «rafale» seulement) est le nombre de courbes (de 2 à 60) définissable par l'ascenseur à droite.

Action

[Nouveau] efface et dessine un graphique complet selon les paramètres définis.
[Ajout] dessine un graphique au dessus d'un autre existant déjà. Attention: dans ce cas, une nouvelle couleur de fond ne sera pas prise en compte.
[Hasard] dessine un graphique selon des paramètres redéfinis au hasard.

"""