INTRODUCTION A SIMULA Jean G. Vaucher (Mis a jour: janvier 1995) Le but de ce document est de permettre a une personne connaissant deja PASCAL d'acquerir rapidement les connaissances necessaires pour comprendre et composer des programmes en SIMULA. PASCAL et SIMULA etant tous deux fortement inspires d'ALGOL 60, la ressemblance de famille est assez forte. Vous trouverez a la fin de ces notes une liste de references sur SIMULA et la documentation UNIX du SIMULA de Lund Software que nous avons sur Sun et Apollo. Pour fins de compatibilite avec UNIX, ce document ne contient aucun accent. 1. UN PROGRAMME COMPLET ======================= Voici un programme complet qui montre l'allure de SIMULA . Ceci nous permettra de faire quelques commentaires generaux. begin ! programme simple ; integer i,j; integer carre ; i:= inint; j:= inint; carre := i*i; outtext(" i carre somme"); outimage; outint(i,5); outint(carre,5); outint(i+j, 6); outimage; end Un programme SIMULA est un BLOC selon la syntaxe : BEGIN < declarations > < enonces > END Un BLOC etant aussi un enonce, on peut imbriquer les blocs et declarer des variables ou on en a besoin: IF B THEN BEGIN COMMENT SWAP I AND J ; INTEGER T; T:=I; I:=J; J:=T END; Noter que les delimiteurs de commentaires ("!" et ";"), et ceux pour les chaines de textes sont differents de ceux de PASCAL. (On peut aussi utiliser le mot-cle "comment" de l'ancien standard au lieu de "!" ). Les entrees/sorties sont assez lourdes; on doit transferer une valeur a la fois. Par contre les enonces d'affectation et expressions sont quasi identiques a PASCAL. Voyons maintenant plus de details. 2. DECLARATIONS DE BASE ======================= Les types de base sont: entier, reel, booleen, caractere, reference (pointeur) et texte (chaine de caracteres). Toute variable est initialisee automatiquement a une valeur neutre: 0, false, none (nil) ou notext (chaine vide). Voici le style des diverses declarations: PASCAL SIMULA CONSTANTES I: integer; integer I; 12 R: real; real R ; 13.4 1.3&-5 long LR; 1.3&&-5 B: boolean; boolean B; true false C: car; character C; 'C' P: ^xxx; ref(xxx) P; none T: alfa; text T; "chaine" On peut declarer des constantes: SIMULA PASCAL integer nMax = 100; const real Pi = 3.1416; nMax = 100; Pi = 3.1416; 3. ENTREE/SORTIES ================= Les entrees/sorties sont en format libre et portent sur les entiers, reels, caracteres et chaines. Pour chaque type on a une fonction pour l'entree et une procedure pour la sortie qui transferent une valeur a la fois. Pour les ecritures, on doit specifier la taille des champs. PASCAL SIMULA read(I,R,C); I:= inint; R:= inreal; C:= inchar; readln; inimage; B:= eof; B:= endfile; (vrai si on est a la fin) B:= lastitem; (on saute les blancs avant de decider) writeln(I,R:10:2,C,'OK'); outint(I,5); outreal(R,2,10); ...gare a l'ordre outchar(C); outtext("OK"); outimage; 4. OPERATEURS ============== Sensiblement les memes, mais on a ** pour puissance (X ** i veut dire Xi) // pour DIV & pour la concatenation de chaines MOD(I,J) = I MOD J Attention au comparaison d'egalite (= et <> en PASCAL) - <> s'ecrit NE - pour les booleens, = s'ecrit EQV (equivalent) - pour les pointeurs on utilise == et =/= SIMULA a 9 niveaux de priorite comme ALGOL, ce qui evite souvent des parentheses. PASCAL SIMULA 1. NOT 1. ** 2. * / DIV MOD AND 2. * / // 3. + - OR 3. + - 4. = <> > >= < <= 4. = NE > >= < <= == =/= 5. NOT 6. AND 7. OR 8. IMPL 9. EQV 5. FONCTIONS STANDARDS ====================== ABS,SIN,COS,LN,SQRT,... comme en PASCAL. Voici les autres: RANK(C) : donne code entier du caractere C CHAR(N) : donne caractere dont le code est N LETTER(C): vrai si C est une lettre DIGIT(C): vrai si C est un chiffre ENTIER(R): donne entier maximum dont valeur <= R ( un peu different de TRUNC) Noter que par defaut, l'affectation d'une valeur reelle a un entier est arrondie. 6. FONCTIONS PSEUDO ALEATOIRES ============================== On en discute ici pour grouper les fonctions ensemble. Elles sont tres utiles en simulation mais dans une premiere lecture de ce document, on suggere de passer a la section suivante. Dans ce qui suit, U est un entier, dit GERME, qui est modifie a chaque appel de procedure. La suite de valeurs generee semble aleatoire mais en fait depend de la valeur initiale de U. U devrait etre initialise a une valeur impaire. BOOLEAN PROCEDURE DRAW (P,U) : donne TRUE avec probabilite P, ou 0 <= P <= 1. INTEGER PROCEDURE RANDINT(A,B,U) : retourne entier dans l'intervalle [A,B] REAL PROCEDURE UNIFORM (A,B,U) : retourne reel uniformement reparti dans [A,B[ REAL PROCEDURE NORMAL (A,B,U) : distribution normal avec moyenne A et ecart type B REAL PROCEDURE NEGEXP (A,U) : distribution negative exponentielle avec moyenne 1/A. Utilisee souvent pour l'intervalle de temps entre deux arrivees quand on a A arrivees par unite de temps. 7. AUTRES DECLARATIONS ====================== Pascal a une structure tres elaboree de declarations. SIMULA est moins riche mais plus tolerant. L'ordre des declarations importe peu et on n'a pas besoin d'eviter les references FORWARD. -Les etiquettes SIMULA sont des identificateurs declares implicitement par leur apparition devant un enonce: L: ;........; GOTO L; -Les tableaux SIMULA sont dynamiques et les bornes peuvent etre des expressions quelconques qui sont evaluees a l'entree du bloc ou le tableau est declare. PASCAL: A,B : ARRAY [1..10,2..5] OF REAL; SIMULA: REAL ARRAY A,B (1:10,2:5); Mais aussi: BEGIN INTEGER N; ..... N:=10; BEGIN INTEGER ARRAY A (1:N); INTEGER ARRAY B (1+ININT : 2*N); ... END END Par contre, les index sont limites a des valeurs entieres et l'affectation entre tableaux ( A:= B ) n'est pas permise. -Le seul constructeur de type, CLASS, declare des types de structures dynamiques. On montre ici une application simple mais nous verrons que le concept de CLASS fait la force de SIMULA. SIMULA PASCAL CLASS COMPLEX; TYPE BEGIN COMPLEX= STRUCT REAL R,I; R,I: REAL; END; END; VAR REF(COMPLEX) C; C: ^COMPLEX; ... ... C:- NEW COMPLEX; NEW (C); 8. ENONCES ========== AFFECTATION: I:= I+1; mais aussi, I:= J:= K:= I+1; pour les pointeurs on utilise ":-" : P1 :- P2; IF: if C then else Note: on permet aussi des expressions conditionnelles: I := if A>B then I+1 else I-1; BLOC: begin ; end GOTO: TOTO:......; goto TOTO; WHILE: while I<10 do I:=I+1; FOR En gros il y a la boucle avec un pas (variable) et celle avec une liste de valeurs. Avec une liste, la variable de controle peut etre de type arithmetique, caractere, texte, booleen, ou pointeur. for I:= 1 step I+1 until 100 do ...; for I:= 100 step -1 until 1 do ...; for I:= 1,I+3 while I**2 < 100 do ...; for I:= 2,3,5,7,11 step 4 until 51 do ...; for C:= 'A','E','I','O','U','Y' do ... ; for P:- P1,P2,P3 do ...; 9. PROCEDURES ============= Par defaut, les parametres simples sont passes par valeur et les tableaux sont passes par reference. On peut modifier ceci avec les listes VALUE et NAME (noter que le NAME d'ALGOL est plus puissant que le VAR de PASCAL). Pour plus de details voir SIMULA REF MANUAL p. 3.62. PASCAL: PROCEDURE ADD (A,B: INTEGER; VAR C:INTEGER); BEGIN C:= A+B; END; FUNCTION SUM (A,B:INTEGER) : INTEGER; BEGIN SUM:= A+B END; SIMULA: PROCEDURE ADD (A,B,C); NAME C; INTEGER A,B,C; BEGIN C:= A+B; END; INTEGER PROCEDURE SUM (A,B); INTEGER A,B; SUM:= A+B; L'utilisation dans les deux cas serait: ADD (1, SUM(I,J), K); Le corps d'une procedure est un seul enonce (voir SUM). Assez souvent cet enonce est un bloc (voir ADD). Quand on passe un tableau, on ne doit pas specifier les bornes: PROCEDURE MATMUL (A,B,C) ; REAL ARRAY A,B,C ; Voici maintenant plus de details. En SIMULA, il y a 3 modes de passage de parametres: par VALUE : comme en PASCAL par REFerence: a peu pres comme le VAR de PASCAL par NAME : comme en ALGOL 60 Le passage par nom (NAME) est equivalent a un remplacement textuel de chaque occurence d'un parametre formel (PF) par le texte du parametre actuel (PA) correspondant. Chaque reference a un PF implique donc une re-evaluation du PA. Avec le passage par valeur ou par reference, il existe pour chaque PF une variable locale initialisee selon la valeur du PA calculee une fois a l'appel de la procedure (ou creation d'objet). Par defaut, les variables simples (int,real,bool et char) sont passees par valeur et les autres par REFerence. Le mode par defaut pour un parametre peut etre change en mettant l'identificateur du parametre dans des listes NAME ou VALUE avant la specification du type des parametres. Pour les classes, NAME ne peut pas etre employe comme mode de passage. On ne peut pas non plus passer des procedures, etiquettes ou switch. Ceci evite la possibilite de reference a des variables locales de blocs disparus. Voici 2 tableaux qui resument les modes de passage possibles. Un "D" indique le mode par Defaut, un "O" indique un mode Optionnel obtenu avec une specification NAME ou VALUE, et "X" indique un mode impossible ou defendu. PARAMETRES DE PROCEDURES mode de passage type de parametre VALUE REF NAME - real,int,bool ou char D X O - text ou array de (real,int,bool,char) O D O - ref(..) ou array de (ref,text) X D O - procedure,label ou switch X D O PARAMETRES DE CLASS mode de passage type de parametre VALUE REF - real,int,bool ou char D X - text ou array de (real,int,bool,char) O D - ref(..) ou array de (ref,text) X D Ces tableaux semblent a premiere vue un peu compliques sans raison. Par exemple, pourquoi ne peut-on pas passer une REFerence par VALUE ou quelle est la difference entre le passage d'un texte par VALUE ou par REFerence? La reponse est que la notion de passage par valeur, dans le cas d'une structure de liste ou de structures pointees, est quelque peu ambigue. Le passage par valeur est suppose empecher les effets de bords en creant une copie locale du PA. En fait le passage par value d'un pointeur en PASCAL protege efficacement la variable pointeur passee comme parametre; mais le contenu de l'objet pointe n'est nullement protege. Dans ce cas, un VRAI passage par valeur exigerait une copie complete non seulement de l'objet pointe mais aussi de tout autre objet atteignable par des pointeurs dans cet objet. Pour les pointeurs (variables REF), ce qui serait appele en PASCAL le passage par valeur est appele en SIMULA passage par reference et le passage par VALUE est interdit, C.A.D. le pointeur utilise en parametre est protege, mais la structure pointee ne l'est pas. A peu pres la meme chose s'applique au TEXTs. Une variable TEXT en SIMULA est un descripteur qui contient, entre autre, un pointeur vers une chaine de caracteres et un indicateur, POS, de l'endroit ou on est rendu dans le traitement de cette chaine. Dans ce cas, le passage par defaut (mode REF) implique une copie locale du descripteur: le descripteur (PA) est donc protege mais la chaine elle meme peut etre changee. Pour le passage par VALUE, on fait une copie et de la chaine et du descripteur (avec POS remis a 0). 10. CHAINES =========== (SAUTER EN PREMIERE LECTURE) Les chaines de caracteres en SIMULA sont gerees dynamiquement et les procedures d'acces et de traitement des chaines favorisent le traitement sequentiel des caracteres. On remarquera donc une analogie aux entrees/sorties. A chaque chaine est associe un descripteur contenant l'adresse de la chaine, sa longueur et l'index du prochain caractere a traiter. Une variable de type TEXT designe ce descripteur. Initiallement, les variables de texte designent NOTEXT, la chaine vide (equivalent a NONE). La creation de chaines se fait avec une de deux fonctions: BLANKS(N) : qui cree une chaine de N caracteres blancs COPY (T) : qui cree une copie de T. TEXT T1,T2; ... T1 :- BLANKS(80); T2 :- COPY("** IFT 3040 **"); Les operateurs d'affectation et de comparaison peuvent porter sur les descripteurs ( :-, ==, =/= ) ou sur le contenu des chaines ( :=, =, NE ). Voici l'quivalent PASCAL ou on aurait T1,T2 : ^ ALFA . SIMULA PASCAL T1:- T2; T1 := T2; T1:= T2; T1^:= T2^; T1 == T2 T1 = T2 T1 NE T2 T1^ <> T2^ On peut aussi tester l'ordre lexicographique de chaines avec >,>=,< et <=. Nouveaute: recemment un operateur de concatenation de chaine ( & ) a ete introduit dans le langage. Exemple: text T; ... outtext( "Error:" & T & "in system") ; La manipulation du contenu de chaines se fait a l'aide de procedures et fonctions et la notation (que nous reverrons avec les CLASS) est celle utilisee en PASCAL pour l'acces a des champs de STRUCT: la notation de point. T.LENGTH: donne la longueur de la chaine T T.POS: donne l'index du prochain caractere a traiter T.MORE: TRUE si T.POS > T.LENGTH, FALSE autrement T.GETCHAR: retourne prochain caractere et incremente T.POS T.PUTCHAR(C): place le caractere C a l'endroit POS et incremente POS. Remarquer la ressemblance avec INCHAR et OUTCHAR. T.SETPOS(N): donne la valeur N a T.POS T.GETINT et T.PUTINT(I): a peu pres equivalent a ININT et OUTINT T.SUB(I,N): designe une sous chaine de T de longueur N debutant a la position I. Remarquer qu'on ne cree pas de copie. SUB est utile pour avoir acces au champs de record formates. T.STRIP: retourne la sous chaine T.SUB(1,N) ou tous les caracteres apres le n-ieme sont blancs. 11. CLASS ========= La puissance de SIMULA reside dans son concept de CLASS. Une declaration de CLASS definit un type d'OBJET dynamique. Ces objets ressemblent au RECORDs de PASCAL dans le sens qu'ils ont des attributs (champs) accessibles par notation pointee (ex: P.A ) et ils ressemblent aux procedures dans le sens qu'une CLASS peut avoir des enonces et des parametres. En plus, la PREFIXATION permet le traitement rationnel d'objets avec variantes et les objets peuvent fonctionner en COROUTINES. Finallement, SIMULA gere l'allocation dynamique de facon tres securitaire en initialisant tout pointeur a NONE et en recuperant automatiquement l'espace occupe par des objets inaccessibles. Prenons un premier exemple graphique. Un POINT est defini par ses coordonnees rectangulaires X et Y. Un autre attribut est sa distance DIST de l'origine. On peut declarer le type POINT avec CLASS POINT; BEGIN REAL X,Y,DIST; END; Les objets specifies par des classes doivent etre crees dynamiquement par l'operateur NEW et on les accede par des pointeurs (REFERENCEs). REF(POINT) P; ... P:- NEW POINT; P.X:= 1; P.Y:= 3.0; P.DIST:= SQRT (P.X**2 + P.Y**2); Voyons l'analogie avec les procedures avec une autre version de POINT. Ici on met X et Y en position parametres pour exiger une valeur initiale a la creation et on met un enonce pour calculer la veleur de DIST. CLASS POINT (X,Y); REAL X,Y; BEGIN REAL DIST; DIST := SQRT (X**2+Y**2); END; P :- NEW POINT (1.0,3.0); A la creation, X et Y recoivent les valeurs 1 et 3 et le corps de POINT est execute avant de revenir a l'enonce de creation. POINT fonctionne comme une procedure a la difference que le "bloc d'activation" n'est pas detruit au retour et qu'on peut acceder au variables locales par le pointeur: P.X, P.Y et P.DIST . Les attributs accessibles d'un objet ne sont pas limites a des variables; on peut aussi avoir des procedures et fonctions locales. Ceci permet d'integrer les operations permises sur un type d'objet a la declaration de sa classe. Ici l'attribut DIST depend des valeurs X et Y et un changement de X ou Y entraine un changement de DIST. Il est plus elegant de declarer DIST comme attribut fonctionnel qui calcule la bonne valeur chaque fois qu'on en a besoin: CLASS POINT (X,Y); REAL X,Y; BEGIN REAL PROCEDURE DIST; BEGIN DIST:= SQRT(X**2 + Y**2) END; END; La distance du point P est donnee par P.DIST et remarquer que DIST etant une fonction on ne peut pas lui affecter une valeur avec P.DIST:=5 . 12. VARIANTES D'OBJETS ====================== Soient trois types d'objets T1, T2 et T3 ou chacun a l'attribut X mais T2 a l'attribut Y en plus et T3 a l'attribut Z en plus de X et Y. PASCAL traiterait ceci avec un RECORD a variantes. SIMULA utilise la PREFIXATION pour faire ressortir la hierarchie des types. On declare CLASS T1; BEGIN REAL X ; END; Pour T2 on declare une class contenant seulement l'attribut additionnel Y et on indique que T2 a tous les attributs de T1 en mettant l'identificateur T1 comme prefixe a la declaration: T1 CLASS T2; BEGIN REAL Y; END; De meme T2 CLASS T3; BEGIN REAL Z; END; En fait la prefixation ajoute a la classe prefixee tous les variables, declarations, parametres, procedures et enonces du prefixe. Dans notre exemple graphique, prenons le type SEGMENT defini par un point, une longueur R et un angle THETA. Disons qu'on veut aussi definir une operation SHIFT sur les segments qui les deplace en X et Y. POINT CLASS SEGMENT (R,THETA); REAL R,THETA; BEGIN PROCEDURE SHIFT(DX,DY); REAL DX,DY; BEGIN X:=X+DX; Y:= Y+DY; END; END; REF (SEGMENT) S; A la creation, il faut fournir les parametres du prefixe ainsi que ceux de la classe prefixee: S:- NEW SEGMENT (1,3,10,PI); S.SHIFT(-10,+4); 13. QUALIFICATION ================= Par qualification on entend le type des references. Tout comme PASCAL utilise la notion de type pour faire beaucoup de verifications des la compilation, SIMULA utilise la qualification pour depister des erreurs, augmenter la securite et l'efficacite. Soit CLASS T1; BEGIN REAL X; END; CLASS C ; BEGIN REAL TOTO; END; REF(T1) P1; REF(C) PC; A la compilation, on traitera P1:-PC comme une erreur et de meme avec P1.TOTO ou PC.X . Avec la prefixation, le controle doit etre plus fin et SIMULA vise un compromis entre securite et efficacite. Soit T1 CLASS T2; BEGIN REAL Y; END; T2 CLASS T3; BEGIN REAL Z; END; REF(T2) P2; REF(T3) P3; Les classes T2 et T3 sont des raffinements de T1 et les objets de T1, T2 et T3 ont tous l'attribut X. On permet donc a P1 de designer des objets T1,T2 ou T3 et dans tous cas P1.X sera permis. Par contre, pour eviter des verifications de types a l'execution, on interdit P1.Y et P1.Z . De meme, on permet P2.X et P2.Y mais P2.Z est interdit, etc... Noter que meme si P1.X est correct syntaxiquement et semantiquement, l'acces P1.X peut mener a une erreur a l'execution si P1==NONE. Pour l'affectation entre pointeurs de qualifications reliees, SIMULA est plus tolerant et fait les verifications necessaires a l'execution: P1:- P3; (aucune verification necessaire) P3:- P1; (verification que P1 designe un T3 ou NONE) L'usager peut verifier le type des objets designes avec IS et IN: P IS C : vrai si l'objet designe par P est de type C (cree par NEW C), et faux autrement ou si P==NONE. P IN C: VRAI si l'objet designe est de type C ou si C apparait dans sa suite de prefixes. On peut aussi forcer la verification et modification de qualification avec QUA. P1 QUA T3 . Z est equivalent a ref(T3) temp; temp:-P1; temp.Z Il est souvent utile a un objet de s'autoreferencer (c.a.d. d'obtenir un pointeur sur lui-meme). On pourrait imaginer une fonction du style MOI, mais SIMULA prefere indiquer explicitement la qualification de cette autoreference avec THIS . Par exemple, dans T3, on pourrait ecrire THIS T1, THIS T2 ou THIS T3. Finallement regardons l'enonce INSPECT dont l'intention est le meme que le WITH de PASCAL: alleger l'ecriture. Au lieu d'ecrire P1.X := P1.X +1; on peut dire INSPECT P1 DO X:=X+1 ; ou si P1==NONE, l'enonce est saute. Une clause OTHERWISE permet de traiter le cas ou P1 est NONE et on peut aussi utiliser les clauses WHEN pour traiter les variantes. INSPECT P1 WHEN T1 DO X:=3 WHEN T2 DO X:= Y+6 WHEN T3 DO X:= Y+Z OTHERWISE OUTTEXT(" P1 EST NONE "); 14. SIMSET ========== Nous avons vu que la prefixation d'une classe par le nom d'une autre lui ajoutait tous les attributs, declarations et enonces du prefixe. La prefixation s'applique aussi au BLOCs et aux programmes. Par ce biais, SIMULA permet une extension du langage. SIMULA a deux classes predefinies, SIMSET et SIMULATION, que l'usager peut utiliser comme prefixe. SIMSET contient des declarations utiles pour le traitement de listes et SIMULATION sert a la simulation. *Les listes de SIMSET sont circulaires a deux sens avec tetes de listes. Trois classes sont definies: HEAD qui decrit les tetes de listes et comprend les procedures et fonctions s'appliquant aux listes, par exemple FIRST pour donner le premier element d'une liste; LINK que l'usager utilise comme prefixe aux objets qui seront membres de listes; et LINKAGE, prefixe commun a HEAD et LINK afin de permettre le chainage. Voici le squelette de SIMSET. CLASS SIMSET; BEGIN CLASS LINKAGE; BEGIN REF(LINK) PROC SUC ; ...; REF(LINK) PROC PRED; ...; REF(LINKAGE) PROC PREV; ...; END; LINKAGE CLASS HEAD; BEGIN REF(LINK) PROC FIRST ; ...; REF(LINK) PROC LAST ; ...; BOOLEAN PROC EMPTY ; ...; INTEGER PROC CARDINAL;..; PROC CLEAR ; ...; END; LINKAGE CLASS LINK; BEGIN PROC OUT ;... PROC FOLLOW (X); REF(LINKAGE) X; ... PROC PRECEDE(X); REF(LINKAGE) X; ... PROC INTO (L); REF(HEAD) L ; ... END; END; Les noms sont assez evocateurs des operations implantees. Remarquons qu'un LINK ne peut etre que dans une seule liste a la fois. Un objet dans une liste qui est insere dans une autre en sera retire au prealable. En general, une operation sans sens comme mettre un objet devant un autre qui est soit NONE soit hors d'une liste ne donne pas d'erreur; l'operation n'a pas d'effet. Soit REF(HEAD) L et REF(LINK) A,B, L.EMPTY donne VRAI si L est vide L.FIRST donne le premier element de la liste L (ou NONE) L.LAST donne le dernier element L.CARDINAL donne le nombre d'elements dans L L.CLEAR vide la liste A.SUC donne le successeur de A s'il existe, mais L.LAST.SUC donne NONE A.PRED donne le predecesseur (L.FIRST.PRED==NONE) A.PREV donne le predecesseur qu'il soit HEAD OU LINK A.OUT sort A de sa liste s'il y est A.FOLLOW(B) met A derriere B A.PRECEDE(B) met A devant B A.INTO(L) met A en queue de la liste L Voici un exemple d'un programme qui utilise SIMSET pour imprimer a l'envers tous les caracteres non blancs sur INPUT. SIMSET BEGIN LINK CLASS ELEMENT (C); CHARACTER C; ; REF(HEAD) LISTE; REF(ELEMENT) P; LISTE :- NEW HEAD; WHILE NOT LASTITEM DO NEW ELEMENT( INCHAR ) . INTO (LISTE); P:- LISTE.LAST; WHILE P=/= NONE DO BEGIN OUTCHAR(P.C); P:-P.PRED; END; END 15. COROUTINES ============== Les objets de SIMULA fonctionnent comme des coroutines. Pour comprendre les coroutines, regardons d'abord comment marche l'appel de procedure. Quand une procedure P1 appelle une procedure P2: 1) une copie de P2 est cree, 2) le controle passe au debut de P2, 3) P2 execute jusqu'a la fin, 4) on detruit P2 et 5) le controle revient a P1. Il existe une relation maitre-esclave entre P1 et P2. Avec deux coroutines P1 et P2, le passage de controle se fait d'egal a egal. Pour passer le controle a P2, P1 fera RESUME (P2). Pour revenir a P1, P2 fera RESUME(P1). Noter qu'une coroutine activee par RESUME continue la ou elle etait quand elle a passe le controle a quelqu'un d'autre, pas au debut. Noter aussi qu'un "retour" n'implique pas la destruction de la routine. La situation est legerement compliquee par le probleme de creation et de fin des coroutines. Quand une coroutine passe a travers son END final, a qui passer le controle? Pour expliquer ceci, on suppose que chaque coroutine a un pointeur cache RETOUR qui peut designer soit une autre objet, soit le bloc englobant ou se trouve sa declaration. Un objet passe le controle a RETOUR avec l'intruction DETACH qui a l'effet suivant: temp :- RETOUR; RETOUR:- "bloc englobant"; RESUME (temp); Notons que la fin d'une coroutine est equivalente a un DETACH. Maintenant voyons la creation. Quand P1 execute P2 :- NEW C; ceci est equivalent a temp :- new C ; temp.RETOUR :- P1; RESUME (temp); P2:- temp; Dans les exemples que nous avons vu, les objets crees ne faisaient ni RESUME ni DETACH mais executaient jusqu'au END final et le retour etait effectue a l'enonce de creation. Noter que dans un objet qui fait plusieurs DETACH, le premier rend le controle au createur mais les autres rendront le controle au bloc englobant, normallement le programme principal. La derniere instruction de controle entre coroutines est CALL. Utilisee avec DETACH cette instruction permet le controle procedural entre coroutines. C'est a dire, une coroutine peut retourner a celle qui l'a activee sans la nommer explicitement. L'effet de CALL(P2) par P1 est le suivant: P2.RETOUR:- P1; RESUME(P2); On remarque que l'execution de DETACH par P2 rend le controle a P1. Utilisees de facon disciplinee, les instructions RESUME, CALL et DETACH permettent souvent une structuration souple et elegante de programmes complexes. Par contre comme avec les GOTO, on peut facilement creer la pagaille. Il sera donc souvent preferable d'utiliser les procedures de controle implantees dans la classe SIMULATION (a partir de RESUME etc... evidemment). Voici un exemple d'un jeu avec N joueurs. Chacun, a son tour, roule un de et cumule les points. Le premier arrive a 100 gagne. BEGIN INTEGER U,N; U:= 12345; N:= ININT; BEGIN REF(J) ARRAY JOUEUR (1:N); INTEGER I; CLASS J (MOI); INTEGER MOI; BEGIN INTEGER SUIVANT, SUM; SUIVANT := MOD(MOI,N)+1; DETACH; WHILE SUM < 100 DO BEGIN SUM := SUM + RANDINT (1,6,U); RESUME(JOUEUR(SUIVANT)); END; OUTINT(MOI,5); OUTTEXT(" A GAGNE"); OUTIMAGE; END; FOR I:= 1 STEP 1 UNTIL N DO JOUEUR(I) :- NEW J (I); RESUME(JOUEUR(1)); OUTTEXT("-- FIN DU JEU --"); END; END 16. SIMULATION ============== SIMULATION est une classe standard qui introduit le concept de PROCESS afin de decrire des objets dont la synchronisation est basee sur l'evolution du temps. Avec cette class comme prefixe, l'usager n'utilise plus les primitives RESUME, DETACH et CALL qui font des transferts de controle explicites. Par contre, des procedures de plus haut niveau sont fournies pour qu'un PROCESS qui veut s'arreter temporairement puisse indiquer l'heure (EVTIME) a laquelle il desire continuer. Tous les PROCESS actifs sont chaines dans un echeancier SQS par ordre croissant de EVTIME et l'arret d'un process entraine l'activation automatique du process en tete de SQS. (Noter que le "temps" est simule et n'a aucun raport avec l'ecoulement reel du temps de calcul; les evtimes servent a imposer une relation d'ordre entre le phases de calcul des differents process). Voici le squelette de la classe SIMULATION. SIMSET CLASS SIMULATION; BEGIN LINK CLASS PROCESS; BEGIN BOOL PROC TERMINATED;... BOOL PROC IDLE ;... REAL PROC EVTIME ;... REF(PROCESS) PROC NEXTEV;... DETACH;.... END; { REF(HEAD) SQS ; } REF(PROCESS) PROC CURRENT;... REAL PROC TIME ;... REF(PROCESS) MAIN ; PROC HOLD (DT); REAL DT;... PROC PASSIVATE ;... PROC WAIT (L); REF(HEAD) L;... PROC CANCEL(P); REF(PROCESS) P;... END; NOTES: -SIMULATION est prefixe par SIMSET pour donner acces au traitement de listes. -Un PROCESS est prefixe par LINK mais ce ne sont pas ces liens qui servent a chainer les processus actifs dans SQS. Le chainage dans SQS est indirect, par le biais d'un objet invisible de type "event-notice". Un process peut donc etre a la fois dans SQS et une seule autre liste. L'expression P.SUC donne le successeur de P (s'il existe) dans cette autre liste; pour trouver le process suivant dans SQS, on utilisera P.NEXTEV. -Pour raison de securite, la liste SQS est inaccessible a l'usager.Par contre CURRENT est une fonction qui retourne une reference sur le premier process de SQS et NEXTEV permet de la balayer. Par definition, le premier process est celui qui est en execution. -TIME retourne CURRENT.EVTIME, c.a.d. l'heure courante. -Le programme peut agir comme un process et il est designe par MAIN. Voyons les attributs de PROCESS. Soit REF(PROCESS) P, P.IDLE : TRUE si P est hors de SQS P.TERMINATED: TRUE si P a passe son END final P.EVTIME : erreur si P.IDLE, l'heure de reactivation de P autrement P.NEXTEV : successeur de P dans SQS ou NONE. Le premier enonce de PROCESS est DETACH. La creation d'un process ne le fait donc pas demarrer. SIMULATION contient aussi un certain nombre de procedures de "scheduling" qui manipulent les process dans SQS. Ces procedures operent par defaut sur CURRENT et toutes se terminent en passant le controle au premier process de SQS (en general un nouveau CURRENT). HOLD (DT); ajoute DT a current.evtime et deplace current en consequence PASSIVATE; enleve CURRENT de SQS CANCEL(P); enleve P de SQS WAIT(L); equivalent a: CURRENT.INTO(L); PASSIVATE; Il existe aussi 2 enonces de scheduling, REACTIVATE et ACTIVATE. Ces enonces ont plusieures options. Soit REF(PROCESS) P,P1 : REACTIVATE P AT T; P est cedule (place dans SQS) pour l'heure T REACTIVATE P DELAY DT; P est cedule pour l'heure TIME+DT REACTIVATE P BEFORE P2; REACTIVATE P AFTER P2; P est cedule devant ou apres P2 avec la meme heure que P2 REACTIVATE P; P est place devant current et prend le controle immediatement Avec les options AT et DELAY, P est place derriere tout autre process cedule pour la meme heure (FIFO). Cependant, on peut demander a le mettre devant les autres avec le mot cle PRIOR: REACTIVATE P DELAY 10 PRIOR; Noter que les trois enonces suivants ont tous le meme effet: REACTIVATE P DELAY 0 PRIOR; REACTIVATE P; REACTIVATE P BEFORE CURRENT; Les cas bizarres, P==NONE, P2==NONE, P2.IDLE, P==P2 etc... ne produisent aucun effet. Le deuxieme enonce d'activation ACTIVATE est identique a REACTIVATE si P.IDLE est vrai (P n'est pas dans SQS), autrement si P est deja dans SQS, ACTIVATE n'a aucun effet et P reste ou il etait. PARALLELISME. Les processus de SIMULA travaillent en quasi-parallele: a tout moment, un seul est actif. De plus, un processus qui n'appelle pas de procedure ou d'enonce de scheduling est ininterruptible et TIME reste fixe durant son execution. Pour modelliser le fait que le travail d'un processus prend un certain temps et que d'autres travaillent en parallele, il faut inserer des enonces HOLD(...) aux endroits appropries ou ... represente la "duree" des actions du processus. L'exemple suivant demontre les problemes de synchronisation dans les systemes d'exploitation. On simule 5 processus qui incrementent chacun la variable globale N 10 fois. On s'attendrait a ce que N vaille 50 a la fin de la simulation. En fait, a cause du delai entre l'acces et la mise a jour de N, cette valeur sera moindre. On simule ce delai avec une procedure TRANSFER qui fait un HOLD aleatoire et on insere un HOLD dans le processus. simulation begin integer i,n,u; integer procedure transfer(i); integer i; begin transfer := i; hold(uniform(0,2,u)); end; process class proc; begin integer k; for k:= 1 step 1 until 10 do begin n:= transfer(n+1); hold(1); end; end; u:= 47; for i:= 1 step 1 until 5 do activate new proc; outtext("n devrait valoir 50"); outimage; hold(10000); outtext("n="); outint(n,5); outimage; end; L'execution donne N DEVRAIT VALOIR 50 N= 14 ===================== REFERENCES ===================== 1. Birtwistle, G.M., O.-J. Dahl, B. Myhrhaug and K. Nygaard. RSIMULA Begin.S p. 391, AUERBACH Publishers Inc, 1973. 2. Dahl, O.-J. and C.A.R. Hoare. RHierarchical Program Structures.S Structured Programming, (Ed.) C. A. R. Hoare, p. 175-220, London, Academic Press, 1972. 3. Dahl, O.-J., B. Myhrhaug and K. Nygaard. "SIMULA-67 Common Base Language." Technical Report N< S-22, Norwegian Computer Centre, Oslo, Norway, 1970. 4. Kirkerud, B. "Object-Oriented Programming sith SIMULA." p. 515, Addison-Wesley, 1989. 5. Lapalme, Bronsard, "SIMULA-67 par l'exemple", Document de travail #144, Dept. IRO, Universite de Montreal, octobre 1983. 6. Nygaard, K. "Basic Concepts in Object Oriented Programming." SIGPLAN Notices, 1986 (October), p. 128-132, 1986. 7. Pooley, R.J. "An Introduction to Programming in SIMULA." p. 333, Oxford, Blackwell Scientific Publications, 1987. 8. SIS. "Data Processing, Programming languages: SIMULA." p. 178, Swedish Standards Institute, 1987. 9. Vaucher, La programmation avec SIMULA 67, env. 200 pages (1973), document iro #36 (disponible: secretariat V-240 ou bibliotheque d'info) REFERENCE ELECTRONIQUE: - Simula 4.11 Reference: Macintosh Hypercard stack disponible sous format HQX dans le repertoire ~dift2240/pub/Simula. ============================================= ANNEXE 1: Les compilateurs de SIMULA ============================================= Au Departement, nous avons utilise SIMULA depuis 1970 sur toute une gamme de machines differentes. L'existence d'un comite de standardisation fort depuis la naissance de Simula a fait que des programmes ecrits en 1970 etait toujours executables sur Silicon Graphics. Presentement, sur JSP, nous avons installe CIM, un systeme portable developpe a l'Institut d'informatique d'Oslo qui compile vers C est ensuite utilise le compilateur local de C pour generer du code machine. Ce systeme est distribue gratuitement selon les modalites de GNU du Open Software Foundation. Il a ete porte sur de nombreuses machines qui supportent UNIX, en particulier des gros PC sous LINUX. Sur les Suns du DIRO, nous avons un compilateur industriel fait par Lund AS. de Suede avec generateur de code natif et debugger. Lund a aussi rendu disponible une version MAC, mais celle ci necessite que l'usager possede l'environnement de developpement MPW de Apple. Pour l'information de base sur ces systemes, faire: $ man cim ... sur JSP ou $ man simula ... sur Suns Pour une information plus complete sur CIM, voir aussi "CIM user guide" dans ~dift2240/pub/Simula/cim.guide. En particulier, ce guide indique comment des procedures externes en C. Ci-dessous, tire du "guide", nous donnons l'ABC de la compilation et de l'execution d'un programme CIM sur JSP. 1) Create a SIMULA program with a text editor, and give it a name with extension ``sim''. In this example we name it "doesit.sim": begin Outtext("Simula does it in C"); Outimage; end 2) You can now compile the program with the CIM command: %cim doesit Compiling: doesit.sim cc -w -c doesit.c cc -o doesit doesit.o /usr/local/lib/libcim.a -lm % 3) The compiler will produce C code that is further processed towards machine code with a standard C compiler. All produced files have the same name as the input file, but with different extensions. The compiled and linked program can be invoked by entering: %./doesit Simula does it in C %