reorganisation par cours dans sous-répertoires

This commit is contained in:
2024-02-15 00:00:17 +01:00
parent fb166efe35
commit 0a84502bbd
108 changed files with 125556 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# calcule : 2 + 3 * n
PUSHI 2
LABEL 1
PUSHI 3
ADD
WRITE
DUP
PUSHI 100
INF
JUMPF 2
JUMP 1
LABEL 2
HALT

View File

@@ -0,0 +1,36 @@
# La machine virtuelle à pile.
Une machine pédagogique pour donner une idée de ce qu'est l'assembleur.
Dans le cadre d'un cours de compilation, par exemple en L3 informatique, on peut demander aux étudiants de coder un compilateur qui va transcrire un langage de haut niveau similaire à du python (sans objets et avec quelques types simples entiers, flottants, tableaux) vers du code MVàP.
## Contenu du répertoire.
* add.mvap Un exemple simple de programme
* 3n+2.mvap Un exemple moins simple de programme
* test.mvap Un exemple avec appel de fonction
* MVaP.jar Une archive java qui propose 2 exécutables
MVaPAssembler (assemble en un binaire)
CBaP (interprète le binaire).
* antlr-4.5.2-complete.jar Une archive java contenant de nombreux binaires permettant d'écrire des compilateurs sur lequel le code de la MVàP s'appuit.
## Pour utiliser la MVàP
Il faut avoir java d'installé sur sa machine (c'est le cas si vous passez par proxmox des machines de l'IUT).
** Pour exécuter du code mvap, il faut :
1- Assembler :
$ java -cp antlr-4.5.2-complete.jar:MVaP.jar MVaPAssembler add.mvap
ou avec des traces (option -d) :
$ java -cp antlr-4.5.2-complete.jar:MVaP.jar MVaPAssembler -d add.mvap
ce qui produit le fichier add.mvap.cbap
Le fichier .cbap (code binaire à pile) est du binaire que la machine peut exécuter.
2- Exécuter :
$ java -jar MVaP.jar add.mvap.cbap
ou pour mieux comprendre avec des traces :
$ java -jar MVaP.jar -d add.mvap.cbap

View File

@@ -0,0 +1,28 @@
# lire a et b puis calculer et afficher l'expression ci-dessous.
#[(a+b)*(a-b)]+[(b+1)*(b-1)]
PUSHI 0 # a habite à l'adresse 0
PUSHI 0 # b habite à l'adresse 1
READ
STOREG 0
READ
STOREG 1
PUSHG 0
PUSHG 1
ADD
PUSHG 0
PUSHG 1
SUB
MUL
PUSHG 1
PUSHI 1
ADD
PUSHG 1
PUSHI 1
SUB
MUL
ADD
WRITE
POP
POP # oublier b
POP # oublier a
HALT

View File

@@ -0,0 +1,5 @@
# Boucle infinie
LABEL 0
PUSHI 42
JUMP 0
HALT

View File

@@ -0,0 +1,18 @@
# Boucle While
LABEL 19
# le test qui va laisser une valeur 0 ou 1 en haut de la pile
#PUSHI 0 # pour faux, donc je saute après
#PUSHI 1 # pour vrai, donc je ne saute pas.
READ # on demande à l'utilisateur
JUMPF 18 # saut conditionnel vers le label 1 si le test est Faux sinon continue
# quelque chose
READ
# Fin du quelque chose
JUMP 19
LABEL 18
#La suite
PUSHI 666
WRITE
POP
# Fin de la suite.
HALT

View File

@@ -0,0 +1,13 @@
# Saut qui evite du code
PUSHI 11
PUSHI 12
JUMP 42
PUSHI 666
LABEL 312
PUSHI 111
PUSHI 110
LABEL 42
PUSHI 42
PUSHI 43
PUSHI 44
HALT

View File

@@ -0,0 +1,13 @@
# 11 - 1?
PUSHI 11
PUSHI 1
SUB
# 11 / 2?
PUSHI 11
PUSHI 2
DIV
# tester 3 < 5
PUSHI 3
PUSHI 5
INF
HALT

View File

@@ -0,0 +1,26 @@
# Example du cours de déclaratrion / restauration d'une variable
PUSHI 1 # i habite à l'adresse 0
# quelque chose ici si on veut
PUSHI 42
PUSHI 42
PUSHI 42
POP
POP
POP
# Fin du quelque chose ici si on veut
PUSHI 6
STOREG 0
# quelque chose ici si on veut
PUSHI 42
PUSHI 42
PUSHI 42
POP
POP
POP
# Fin du quelque chose ici si on veut
PUSHI 1
PUSHG 0
ADD
STOREG 0
# i est incrémenté et vaut 7.
HALT

View File

@@ -0,0 +1,19 @@
# exemple du cours.
PUSHI 11
PUSHI 6
PUSHI 15
MUL
SUB
PUSHI 5
ADD
PUSHI 12
ADD
PUSHI 9
PUSHI 4
MUL
PUSHI 7
MUL
ADD
WRITE
POP
HALT

View File

@@ -0,0 +1,42 @@
# Exo.
# j = 1
# i = 0
# i = j
# while i < 10:
# i += 1
# print(i)
#
# étapes par étapes ci-dessous
#
# j = 1
PUSHI 1 ### j habite à l'adresse 0
# i = 0
PUSHI 0 ### i habite à l'adresse 1
# i = j
PUSHG 0 ### récupère j
STOREG 1 ### affecte à i cette valeur
LABEL 12
# test i<10 ?
PUSHG 1 ### récupère i
PUSHI 4 ### pousse 4 pour que la démo tienne sur l'écran au lieu de 10
INF ### <?
#
JUMPF 34
# i += 1
PUSHG 1
PUSHI 1
ADD
STOREG 1
#
JUMP 12
LABEL 34
#La suite
# print(i)
PUSHG 1
WRITE
POP
POP
POP
HALT

View File

@@ -0,0 +1,11 @@
# Moyenne entière de 3 notes 12, 8, 14
PUSHI 12
PUSHI 8
PUSHI 14
ADD
ADD
PUSHI 3
DIV
WRITE
POP
HALT

View File

@@ -0,0 +1,71 @@
### On souhaite comparer 2 parcours en transport public entre l'université de Créteil et l'IUT de SF.
# Parcours 1 :
# prendre la ligne 1 puis 8 pour Gare de Lyon (30 minutes)
# prendre la ligne R pour Fontainebleau-Avon (45 minutes)
# prendre un vélo pour l'IUT (20 minutes).
# vs
# Parcours 2 :
# marcher au Vert de Maison prendre la ligne D pour Melun (55 minutes)
# changer à Melun prendre la ligne R pour Fontainebleau-Avon (16 minutes)
# prendre un vélo pour l'IUT (20 minutes).
#
#
# On souhaite afficher 1 ou 2 selon que le parcours 1 ou parcours 2 est le plus court en temps.
#
# calcul du parcours 1
# PUSHI 30
# PUSHI 45
# PUSHI 20
# ADD
# ADD
# calcul du parcours 2
# PUSHI 55
# PUSHI 16
# PUSHI 20
# ADD
# ADD
# Comparons les deux durées
# INF
# 0 veut dire non, différent de 0 veut dire oui
# HALT
### On a fait le test (30+45+20 < 55+16+20)?
### On veut vraiment faire
### if (30+45+20 < 55+16+20):
### write 1
### else :
### write 2
# calcul du parcours 1
PUSHI 30
PUSHI 45
PUSHI 20
ADD
ADD
# calcul du parcours 2
PUSHI 55
PUSHI 16
PUSHI 20
ADD
ADD
# Comparons les deux durées
INF
# 0 veut dire non, différent de 0 veut dire oui
JUMPF 0
#
# then
PUSHI 1
WRITE
POP
#
JUMP 1
LABEL 0
#
# else
PUSHI 2
WRITE
POP
#
LABEL 1
# la suite
HALT

View File

@@ -0,0 +1,77 @@
### On souhaite comparer 2 parcours en transport public entre l'université de Créteil et l'IUT de SF.
# Parcours 1 :
# prendre la ligne 1 puis 8 pour Gare de Lyon (30 minutes)
# prendre la ligne R pour Fontainebleau-Avon (45 minutes)
# prendre un vélo pour l'IUT (20 minutes).
# vs
# Parcours 2 :
# marcher au Vert de Maison prendre la ligne D pour Melun (55 minutes)
# changer à Melun prendre la ligne R pour Fontainebleau-Avon (16 minutes)
# prendre un vélo pour l'IUT (20 minutes).
#
#
# On souhaite afficher 1 ou 2 selon que le parcours 1 ou parcours 2 est le plus court en temps.
# Puis on souhaite afficher la durée du parcours optimal
#
# A compléter
#
# réserve de la place pour la durée du parcours 1
PUSHI 0
# réserve de la place pour la durée du parcours 2
PUSHI 0
# dp1 habite à l'adresse 0
# dp2 habite à l'addresse 1
#
#
# calcul du parcours 1
PUSHI 30
PUSHI 45
PUSHI 20
ADD
ADD
# dupliquer le résultat pour le stocker dans dp1
DUP
STOREG 0
# calcul du parcours 2
PUSHI 55
PUSHI 16
PUSHI 20
ADD
ADD
# dupliquer le résultat pour le stocker dans dp2
DUP
STOREG 1
# Comparons les deux durées
INF
# 0 veut dire non, différent de 0 veut dire oui
JUMPF 0
#
# then
PUSHI 1
WRITE
POP
# charge la valeur de dp1 en haut de la pile
PUSHG 0
WRITE
POP
#
JUMP 1
LABEL 0
#
# else
PUSHI 2
WRITE
POP
# charge la valeur de dp2 en haut de la pile
PUSHG 1
WRITE
POP
#
LABEL 1
# la suite
#
# vide la mémoire des variables globales dp1 et dp2
POP
POP
HALT

View File

@@ -0,0 +1,74 @@
### On souhaite comparer 2 parcours en transport public avec au plus deux changements
### L'utilisateur doit nous donner ces 6 durées.
# Parcours 1 :
# 3 durées
# vs
# Parcours 2 :
# 3 durées
#
#
# On souhaite afficher 1 ou 2 selon que le parcours 1 ou parcours 2 est le plus court en temps.
# Puis on souhaite afficher la durée du parcours optimal
#
# A compléter
#
# réserve de la place pour la durée du parcours 1
PUSHI 0
# réserve de la place pour la durée du parcours 2
PUSHI 0
# dp1 habite à l'adresse 0
# dp2 habite à l'addresse 1
#
#
# calcul du parcours 1
READ
READ
READ
ADD
ADD
# dupliquer le résultat pour le stocker dans dp1
DUP
STOREG 0
# calcul du parcours 2
READ
READ
READ
ADD
ADD
# dupliquer le résultat pour le stocker dans dp2
DUP
STOREG 1
# Comparons les deux durées
INF
# 0 veut dire non, différent de 0 veut dire oui
JUMPF 0
#
# then
PUSHI 1
WRITE
POP
# charge la valeur de dp1 en haut de la pile
PUSHG 0
WRITE
POP
#
JUMP 1
LABEL 0
#
# else
PUSHI 2
WRITE
POP
# charge la valeur de dp2 en haut de la pile
PUSHG 1
WRITE
POP
#
LABEL 1
# la suite
#
# vide la mémoire des variables globales dp1 et dp2
POP
POP
HALT

View File

@@ -0,0 +1,39 @@
# mon compteur de passages dans la boucle while (donc de valeurs empilees)
PUSHI 0
# Boucle While
LABEL 19
# le test qui va laisser une valeur 0 ou 1 en haut de la pile
READ # on demande à l'utilisateur
JUMPF 18 # saut conditionnel vers le label 1 si le test est Faux sinon continue
# quelque chose
READ
PUSHI 1
PUSHG 0
ADD
STOREG 0
# Fin du quelque chose
JUMP 19
LABEL 18
#
# La suite : il faut faire autant de pop que la valeur du compteur
# en gros while compteur > 0 : Pop; compteur --:
#
LABEL 0
# le test qui va laisser une valeur 0 ou 1 en haut de la pile
PUSHG 0
PUSHI 0
SUP
JUMPF 1 # saut conditionnel vers le label 1 si le test est Faux sinon continue
POP
# decrementer le compter
PUSHG 0
PUSHI 1
SUB
STOREG 0
JUMP 0
LABEL 1
#La suite
POP # pour la variable
# Fin de la suite.
HALT

Binary file not shown.

View File

@@ -0,0 +1,80 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>MVàP</title>
<script src="js/Pile.js" type="text/javascript"></script>
<script src="js/MVaP.js" type="text/javascript"></script>
<style type="text/css">
body{ color: #333; font: 13px 'Lucida Grande', Verdana, sans-serif; }
</style>
</head>
<body>
<h1>Machine Virtuelle à Pile</h1>
<h2>Historique</h2>
Blabla sur la MVàP...
<h2>Documentation</h2>
Toute la doc
<p>
lorem ipsum
<h2>La MVàP</h2>
<h3>Entrez le code</h3>
<div class="runcode">
<textarea id="codeMVaP" name="Code MVàP" label="Enter your code" rows="15" cols="33" spellcheck="false">
PUSHI 2
DUP
PUSHI 3
ADD
PUSHI 4
MUL
MUL
WRITE
POP
HALT
</textarea>
</div>
<h3>Exécution</h3>
<script>
var M = "";
function runCode() {
let text = codeMVaP.value;
let d = debugCheckBox.checked;
let maxSteps = MaxStepsBox.value;
if (M == "") M = new MVaP();
M.load(text);
M.runCode(maxSteps, d);
}
</script>
<input type="button" value="Run" onclick="runCode();"/>
<label for="MaxStepsBox">Nombre maximum de pas (0 pour pas de limite)</label>
<input type="text" id="MaxStepsBox" name="MaxSteps" value="1000" minLength="4" maxLength="8" size="10"/>
<label for="debugCheckBox">Traces pas à pas</label>
<input type="checkbox" id="debugCheckBox" name="debug" checked />
<pre class='traces'>
<input type="button" value="Nettoyer les traces" onclick="cleanTraces()"/>
<div id="tracesBox"></div>
<input type="text" id="inputBox" hidden="true"/>
<div id="errorBox" style="color: red" hidden="true"></div>
</pre>
<script>
function cleanTraces() {
tracesBox.innerText = '';
errorBox.innerText = '';
}
</script>
</body>
</html>

View File

@@ -0,0 +1,216 @@
/**
* Code Binaire pour Machine Virtuelle à Pile.
* Les flottants sont des double qui prennent la place de 2 ints.
* Les adresses sont des int.
* CALL empile l'adresse de retour et la vauler du framePointer dans la pile.
*/
public class CBaP {
pc = 0;
fp = 0;
stack;
program;
const SizeofDouble = 2;
_debug = false;
CBaP(program, debug, stackSize) {
this.program = program;
this._debug = debug;
this.stack = new Pile(stackSize);
}
dumpInstruction(ii, out) {
let i = ii;
switch(program.get_int(i)) {
case AssembleMVaP.ADD: out.print("ADD "); break;
case AssembleMVaP.SUB: out.print("SUB "); break;
case AssembleMVaP.MUL: out.print("MUL "); break;
case AssembleMVaP.DIV: out.print("DIV "); break;
case AssembleMVaP.INF: out.print("INF "); break;
case AssembleMVaP.INFEQ: out.print("INFEQ "); break;
case AssembleMVaP.SUP: out.print("SUP "); break;
case AssembleMVaP.SUPEQ: out.print("SUPEQ "); break;
case AssembleMVaP.EQUAL: out.print("EQUAL "); break;
case AssembleMVaP.NEQ: out.print("NEQ "); break;
case AssembleMVaP.FADD: out.print("FADD "); break;
case AssembleMVaP.FSUB: out.print("FSUB "); break;
case AssembleMVaP.FMUL: out.print("FMUL "); break;
case AssembleMVaP.FDIV: out.print("FDIV "); break;
case AssembleMVaP.FINF: out.print("FINF "); break;
case AssembleMVaP.FINFEQ: out.print("FINFEQ"); break;
case AssembleMVaP.FSUP: out.print("FSUP "); break;
case AssembleMVaP.FSUPEQ: out.print("FSUPEQ"); break;
case AssembleMVaP.FEQUAL: out.print("FEQUAL"); break;
case AssembleMVaP.FNEQ: out.print("FNEQ "); break;
case AssembleMVaP.ITOF: out.print("ITOF "); break;
case AssembleMVaP.FTOI: out.print("FTOI "); break;
case AssembleMVaP.RETURN: out.print("RETURN"); break;
case AssembleMVaP.POP: out.print("POP "); break;
case AssembleMVaP.READ: out.print("READ "); break;
case AssembleMVaP.WRITE: out.print("WRITE "); break;
case AssembleMVaP.WRITEF: out.print("WRITEF"); break;
case AssembleMVaP.PADD: out.print("PADD "); break;
case AssembleMVaP.PUSHGP: out.print("PUSHGP"); break;
case AssembleMVaP.PUSHFP: out.print("PUSHFP"); break;
case AssembleMVaP.DUP: out.print("DUP "); break;
case AssembleMVaP.PUSHI: out.print("PUSHI "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHG: out.print("PUSHG "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STOREG: out.print("STOREG"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHL: out.print("PUSHL "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STOREL: out.print("STOREL"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHR: out.print("PUSHR "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STORER: out.print("STORER"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.FREE: out.print("FREE "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.ALLOC: out.print("ALLOC "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHF: out.print("PUSHF "); ++i; out.format("%7.3f ", program.get_double(++i)); break;
case AssembleMVaP.JUMP: out.print("JUMP "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.JUMPF: out.print("JUMPF "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.JUMPI: out.print("JUMPI "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.CALL: out.print("CALL "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.HALT: out.print("HALT "); break;
default: System.err.println("Code inconnu "+program.get_int(i)+" ligne "+i);
}
if (i == ii) out.print(" ");
return i;
}
public void dumpProgram(PrintStream out) {
// out.println(program.toString(true));
// out.println(program.toString());
out.println(" Adr | Instruction");
out.println("-----+---------------");
for(int i = 0; i < program.getSize(); i++) {
out.format("%4d | ", i);
i = dumpInstruction(i, out);
out.println();
}
}
private void _call(int label) {
push(pc+1);
push(fp);
fp = size();
pc = label;
}
private void _return() {
while(size() > fp)
pop();
fp = pop();
pc = pop();
}
private BufferedReader input = null;
private int _read() {
try {
if (input == null)
input = new BufferedReader(new InputStreamReader(System.in));
return Integer.parseInt(input.readLine());
} catch (Exception e) {
System.err.println(e);
System.exit(1);
return 0;
}
}
public boolean execute() {
if (_debug) {
dumpProgram(System.out);
System.err.println();
System.err.println(" pc | | fp pile");
System.err.println("====================================================");
}
pc = 0;
while (true) {
if (_debug) {
System.err.format("%4d | ", pc);
dumpInstruction(pc, System.err);
System.err.format("| %4d %s\n", fp, stack.toString());
}
try {
int p1, p2; // utile pour être sûr de l'ordre des pop() !
double d1, d2;
switch(program.get_int(pc++)) {
case AssembleMVaP.ADD: p1 = pop(); p2 = pop(); push(p2 + p1); break;
case AssembleMVaP.SUB: p1 = pop(); p2 = pop(); push(p2 - p1); break;
case AssembleMVaP.MUL: p1 = pop(); p2 = pop(); push(p2 * p1); break;
case AssembleMVaP.DIV: p1 = pop(); p2 = pop(); push(p2 / p1); break;
case AssembleMVaP.INF: p1 = pop(); p2 = pop(); push(p2 < p1 ? 1 : 0); break;
case AssembleMVaP.INFEQ: p1 = pop(); p2 = pop(); push(p2 <= p1 ? 1 : 0); break;
case AssembleMVaP.SUP: p1 = pop(); p2 = pop(); push(p2 > p1 ? 1 : 0); break;
case AssembleMVaP.SUPEQ: p1 = pop(); p2 = pop(); push(p2 >= p1 ? 1 : 0); break;
case AssembleMVaP.EQUAL: p1 = pop(); p2 = pop(); push(p2 == p1 ? 1 : 0); break;
case AssembleMVaP.NEQ: p1 = pop(); p2 = pop(); push(p2 != p1 ? 1 : 0); break;
case AssembleMVaP.FADD: d1 = pop_double(); d2 = pop_double(); push(d2 + d1); break;
case AssembleMVaP.FSUB: d1 = pop_double(); d2 = pop_double(); push(d2 - d1); break;
case AssembleMVaP.FMUL: d1 = pop_double(); d2 = pop_double(); push(d2 * d1); break;
case AssembleMVaP.FDIV: d1 = pop_double(); d2 = pop_double(); push(d2 / d1); break;
case AssembleMVaP.FINF: d1 = pop_double(); d2 = pop_double(); push(d2 < d1 ? 1 : 0); break;
case AssembleMVaP.FINFEQ: d1 = pop_double(); d2 = pop_double(); push(d2 <= d1 ? 1 : 0); break;
case AssembleMVaP.FSUP: d1 = pop_double(); d2 = pop_double(); push(d2 > d1 ? 1 : 0); break;
case AssembleMVaP.FSUPEQ: d1 = pop_double(); d2 = pop_double(); push(d2 >= d1 ? 1 : 0); break;
case AssembleMVaP.FEQUAL: d1 = pop_double(); d2 = pop_double(); push(d2 == d1 ? 1 : 0); break;
case AssembleMVaP.FNEQ: d1 = pop_double(); d2 = pop_double(); push(d2 != d1 ? 1 : 0); break;
case AssembleMVaP.FTOI: push((int)pop_double()); break;
case AssembleMVaP.ITOF: push((double)pop()); break;
case AssembleMVaP.RETURN: _return(); break;
case AssembleMVaP.POP: pop(); break;
case AssembleMVaP.READ: push(_read()); break;
case AssembleMVaP.WRITE: System.out.format("%7d\n", peek()); break;
case AssembleMVaP.WRITEF: System.out.format("%7.3f\n", peek_double()); break;
case AssembleMVaP.PADD: p1 = pop(); p2 = pop(); push(p2 + p1); break;
case AssembleMVaP.PUSHGP: push(0); break;
case AssembleMVaP.PUSHFP: push(fp); break;
case AssembleMVaP.DUP: push(peek()); break;
case AssembleMVaP.PUSHI: push(program.get_int(pc++)); break;
case AssembleMVaP.PUSHG: push(get(program.get_int(pc++))); break;
case AssembleMVaP.STOREG: set(program.get_int(pc++), pop()); break;
// ajouter -2 si adresse négative ?
case AssembleMVaP.PUSHL: push(get(fp+program.get_int(pc++))); break;
case AssembleMVaP.STOREL:set(fp+program.get_int(pc++), pop()); break;
case AssembleMVaP.PUSHR: push(get(pop()+program.get_int(pc++)));break;
case AssembleMVaP.STORER:p1 = pop(); p2 = pop(); set(p2+program.get_int(pc++), p1); break;
case AssembleMVaP.FREE: doPop(program.get_int(pc++)); break;
case AssembleMVaP.ALLOC: doPush(program.get_int(pc++)); break;
case AssembleMVaP.PUSHF: push(program.get_int(pc++)); push(program.get_int(pc++)); break;
case AssembleMVaP.JUMP: pc = program.get_int(pc); break;
case AssembleMVaP.JUMPF: if (pop() == 0) pc = program.get_int(pc); else pc++; break;
case AssembleMVaP.JUMPI: pc = program.get_int(pc)+pop(); break;
case AssembleMVaP.CALL: _call(program.get_int(pc)); break;
case AssembleMVaP.HALT: return true;
default: System.err.println("Code inconnu "+program.get_int(pc)+" ligne "+pc); return false;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println(stack.getException());
return false;
}
}
}
void push(int i) { stack.push(i); }
int pop() { return stack.pop_int(); }
void push(double d) { stack.push(d); }
double pop_double() { return stack.pop_double(); }
int peek() { return stack.peek_int(); }
double peek_double() { return stack.peek_double(); }
int size() { return stack.getSize(); }
void set(int index, int i) { stack.set(index, i); }
int get(int index) { return stack.get_int(index); }
void doPop(int nb) { for(; nb > 0; nb--) stack.pop_int(); }
void doPush(int nb) { for(; nb > 0; nb--) stack.push(0); }
}

View File

@@ -0,0 +1,33 @@
digraph LexerMVaP {
Start -> Start [label="space "];
Start -> Id [label="[A-Z]"];
Id -> Id [label="[A-Z]"];
Start -> Int [label="[-+0-9]"];
Int -> Int [label="[0-9]"];
Int -> Decimal [label="."];
Int -> E [label="E"];
Decimal -> Decimal [label="[0-9]"];
Decimal -> E [label="E"];
E -> Exponent [label="[-+0-9]"];
Exponent -> Exponent [label="[0-9]"];
Start -> error [label="*"];
Id -> error [label="*"];
Int -> error [label="*"];
Decimal -> error [label="*"];
E -> error [label="*"];
Exponent -> error [label="*"];
edge [color=red, fontcolor=red];
Start -> Start [label="NL/[Newline] "];
Id -> Start [label="space/ID"];
Int -> Start [label="space/Int"];
Decimal -> Start [label="space/Float"];
Exponent -> Start [label="space/Float"];
{ rank = same; Decimal E Exponent }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -0,0 +1,481 @@
/*
MVàP : Machine Virtuelle à Pile
2 méthodes principales :
load(text) qui charge du texte, l'analyse et l'assemble.
runcode(maxSteps, d) qui exécute le code en limitant le nombre d'étape à maxSteps et qui produit des traces si d est true.
Les sorties se font dans un élément d'id "tracesBox".
Les entrées dans un élément d'id "inputBox".
Les messages d'erreur vont dans un élément d'id "errorBox".
Auteur : Jacques Madelaine
Version 1.0
*/
"use strict";
let debugLexer = false;
let debugAssemble = false;
let debug = true;
/** token lexicaux */
const T_ID = 0;
const T_INT = 1;
const T_FLOAT = 2;
const T_SPACE = 3;
const T_NL = 4;
const T_ERROR = 5;
const T_EOF = 6;
let T_Text = [ "T_ID", "T_INT", "T_FLOAT", "T_SPACE", "T_NL", "T_ERROR", "T_EOF" ];
/** token grammaticaux */
const INSTR1 = 0;
const INSTR2 = 1;
const INT = 2;
const INSTR2F = 3;
const FLOAT = 4;
const LABEL = 5;
const JUMP = 6;
const NEWLINE = 7;
class MVaP {
codes = [ // pour l'interprète de MVàP
[ "ADD", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2+p1); } ],
[ "SUB", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2-p1); } ],
[ "MUL", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2*p1); } ],
[ "DIV", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(Math.floor(p2/p1)); } ],
[ "INF", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2<p1?1:0); } ],
[ "INFEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2<=p1?1:0); } ],
[ "SUP", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2>p1?1:0); } ],
[ "SUPEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2>=p1?1:0); } ],
[ "EQUAL", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2==p1?1:0); } ],
[ "NEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2!=p1?1:0); } ],
[ "FADD", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2+d1); } ],
[ "FSUB", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2-d1); } ],
[ "FMUL", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2*d1); } ],
[ "FDIV", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2/d1); } ],
[ "FINF", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2<d1?1:0); } ],
[ "FINFEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2<=d1?1:0); } ],
[ "FSUP", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2>d1?1:0); } ],
[ "FSUPEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2>=d1?1:0); } ],
[ "FEQUAL", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2==d1?1:0); } ],
[ "FNEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2!=d1?1:0); } ],
[ "ITOF", INSTR1, () => { let p1 = this.stack.pop(); this.trace("ITOF "+p1); this.stack.pushFloat64(p1); } ],
[ "FTOI", INSTR1, () => { this.stack.push(Math.round(this.stack.popFloat64())); } ],
[ "RETURN", INSTR1, () => { while(this.stack.getSize() > this.fp) this.stack.pop();
this.fp = this.stack.pop(); this.pc = this.stack.pop(); } ],
[ "POP", INSTR1, () => { this.stack.pop(); } ],
[ "READ", INSTR1, () => { this.read(); return true; } ],
[ "READF", INSTR1, () => { this.read(); return true; } ],
[ "WRITE", INSTR1, () => { this.trace(this.stack.peek()+"\n"); } ],
[ "WRITEF", INSTR1, () => { this.trace(this.stack.peekFloat64()+"\n"); } ],
[ "PADD", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2+p1); } ],
[ "PUSHGP", INSTR1, () => { this.stack.push(0); } ],
[ "PUSHFP", INSTR1, () => { this.stack.push(this.fp); } ],
[ "DUP", INSTR1, () => { this.stack.push(this.stack.peek()); } ],
[ "PUSHI", INSTR2, () => { this.stack.push(this.program.get(this.pc++)); } ],
[ "PUSHG", INSTR2, () => { this.stack.push(this.stack.get(this.program.get(this.pc++)));} ],
[ "STOREG", INSTR2, () => { this.stack.set(this.program.get(this.pc++), this.stack.pop())} ],
[ "PUSHL", INSTR2, () => { this.stack.push(this.stack.get(this.fp+this.program.get(this.pc++))); } ],
[ "STOREL", INSTR2, () => { this.stack.set(this.fp+this.program.get(this.pc++), this.stack.pop()); } ],
[ "PUSHR", INSTR2, () => { this.stack.push(this.stack.get(this.stack.pop()+this.program.get(this.pc++))); } ],
[ "STORER", INSTR2, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.set(p2+this.program.get(this.pc++)); } ],
[ "FREE", INSTR2, () => { this.stack.doPop(this.program.get(this.pc++)); } ],
[ "ALLOC", INSTR2, () => { this.stack.doPush(this.program.get(this.pc++)); } ],
[ "PUSHF", INSTR2F, () => { this.stack.pushFloat64(this.program.getFloat64(this.pc)); this.pc += 2; } ],
[ "CALL", JUMP, () => { this.stack.push(this.pc+1); this.stack.push(this.fp);
this.fp = this.stack.getSize();
this.pc = this.program.get(this.pc); } ],
[ "JUMP", JUMP, () => { this.pc = this.program.get(this.pc); } ],
[ "JUMPF", JUMP, () => { if (this.stack.pop()==0) this.pc = this.program.get(this.pc);
else this.pc++; } ],
[ "JUMPI", JUMP, () => { this.pc = this.program.get(this.pc)+this.stack.pop();} ],
[ "HALT", INSTR1, () => { return true; } ],
[ "LABEL", LABEL, /* erreur interne */ ]
];
/*** Ajouter instr : POPF */
tokens = []; // indexe le tableau codes sur son premier élément
text = "";
// <Lexer>
// see LexerMVaP.png for the DFA diagram
state = "Start";
text; // le texte à analyser
index = 0; // index dans la chaîne text
lineNumber = 1;
transitions = [
{ state: "Start", next: "Start", test: (c) => { return c == "\n"; }, token: T_NL },
{ state: "Start", next: "Start", test: (c) => { return c.trim() == ""; }, token: false },
{ state: "Start", next: "Id", test: (c) => { return "A" <= c && c <= "Z"; }, token: false },
{ state: "Id", next: "Id", test: (c) => { return "A" <= c && c <= "Z"; }, token: false},
{ state: "Id", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_ID},
{ state: "Start", next: "Int", test: (c) => { return ("0" <= c && c <= "9")||c=="-"||c=="+"; }, token: false },
{ state: "Int", next: "Int", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Int", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_INT },
{ state: "Int", next: "Decimal", test: (c) => { return c == "."; }, token: false },
{ state: "Int", next: "E", test: (c) => { return c == "E"; }, token: false },
{ state: "Decimal", next: "Decimal", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Decimal", next: "E", test: (c) => { return c == "E"; }, token: false },
{ state: "Decimal", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_FLOAT },
{ state: "E", next: "Exponent", test: (c) => { return ("0"<=c && c<="9")||c=="-"||c=="+"; }, token: false },
{ state: "Exponent", next: "Exponent", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Exponent", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_FLOAT }
// else: error
];
initLexer() {
this.index = 0;
this.lineNumber = 1
console.log("*****************\n* C'est parti *\n*****************");
}
// Donne la transition correspondante à l'état courant et au caractère entrant
transition(c) {
for (let v of this.transitions.values()) {
// on aurait pu indexer les transitions sur state au lieu de tout énumérer
if (this.state == v.state && v.test(c)) {
return v;
}
}
return T_ERROR;
}
yytext = ""; // contient le texte du jeton syntaxique (nommé comme dans lex/yacc)
nextToken() {
let c;
let trans;
while (true) {
c = this.text[this.index];
if (c === undefined)
return T_EOF;
c = c.toUpperCase();
trans = this.transition(c);
if (trans == T_ERROR) { this.yytext += c; return T_ERROR; }
if (this.state != trans.next && this.state == "Start") this.yytext ="";
this.state = trans.next;
this.index++;
if (Number.isInteger(trans.token)) {
break;
}
this.yytext += c;
}
// séparateur significatif
if (c == "\n") {
if (trans.token != T_NL) {
this.index--; // ne pas manger la fin de ligne non prise en compte
// une autre façon de dire est que l'on n'incrémente pas l'index sur le renvoi de token
// sauf en cas de token constitué de séparateur
} else
this.lineNumber++; // on en profite pour compter la ligne
}
// console.log(" line " + this.lineNumber + " " + trans.token + " '" + yytex + "' i="+i);
return trans.token;
}
// </Lexer>
// <Analyse sémantique et assemblage>
/** Grammaire des instructions
instr
: INSTR1 T_NL
| INSTR2 T_INT T_NL
| JUMP T_INT T_NL
| INSTR2F T_FLOAT T_NL
| LABEL T_NL
| T_NL
;
*/
program;// Pile contenant le programme compilé
adressesLabels; // adresse des étiquettes pour l'assemblage
analyse() {
this.initLexer();
let token; // le jeton lexical courant
let state = NEWLINE; // état de l'analyseur grammatical (ici le précédent syntagme grammatical)
this.program = new Pile();
this.adressesLabels = [];
while (true) {
this.yytext = "";
token = this.nextToken();
if (debugLexer)
console.log("** nextToken line "+this.lineNumber+" token "+token+" "+T_Text[token]+" '" +this.yytext+"' index "+this.index+" state "+state);
switch(token) {
case T_EOF:
return true;
case T_ID:
let k = this.tokens[this.yytext];
if (k === undefined || state != NEWLINE) return false;
state = this.codes[k][1];
if (state != LABEL) { // pas d'instruction LABEL dans le code
this.program.push(k);
}
break;
case T_INT:
if (state == LABEL) { // on mémorise juste l'adresse pour la passe d'assemblage
this.adressesLabels[Number(this.yytext)] = this.program.getSize(); // ± 1 who knows?
} else if(state == INSTR2 || state == JUMP) {
this.program.push(Number(this.yytext));
} else return false;
state = INT;
break;
case T_FLOAT:
if (state != INSTR2F) return false;
state = FLOAT;
this.program.pushFloat64(Number(this.yytext));
break;
case T_NL:
if (state == INSTR2 || state == JUMP || state == INSTR2F) return false;
state = NEWLINE;
break;
case T_ERROR:
return false;
default:
alert("Erreur interne prévenir la maintenance d'urgence");
return false;
}
}
console.log("Programme correct syntaxiquement");
return true;
}
assemble() {
// change les numéros d'étiquettes par les adresses des instructions pointées.
if (debugAssemble) {
console.log("Label "+this.adressesLabels);
console.log(this.dumpProgram());
}
for (let i = 0; i < this.program.getSize(); i++) {
let k = this.program.get(i);
// Attention il faut compter si par malchance un entier = code d'un saut
switch (this.codes[k][1]) {
case INSTR1:
break;
case INSTR2:
i++; // on saute l'entier qui suit
break;
case INSTR2F:
i += 2; // on saute le flottant qui suit
break;
case JUMP: // met à jour l'adresse du saut
i++;
console.log("--- set "+i);
this.program.set(i, this.adressesLabels[this.program.get(i)]);
break;
default:
alert("Erreur assemblage");
}
}
// console.log(this.dumpProgram());
return true;
}
// </Analyse sémantique et assemblage>
dumpInstruction(i, result) {
let code = this.codes[this.program.get(i)];
result += code[0].padEnd(6, ' ');
switch (code[1]) {
case INSTR2:
case JUMP:
result += this.program.get(++i).toString().padStart(7, ' ');
break;
case INSTR2F:
result += this.program.getFloat64(++i).toString().padStart(7, ' ');
i++;
break;
default:
result += " ";
}
return [i, result];
}
dumpProgram() {
let result = "";
result += this.program + "\n";
result += " Adr | Instruction\n-----+---------------\n";
for(let i = 0; i < this.program.getSize(); i++) {
result += i.toString().padStart(4, " ")+" | ";
[i, result] = this.dumpInstruction(i, result);
result += "\n";
}
return result;
}
constructor() {
// init tokens
for (let k of this.codes.keys())
this.tokens[this.codes[k][0]] = k;
try {
inputBox.addEventListener("keydown", (event) => { this.enterText(event); });
} catch(e) {
alert("Warning READ indisponible");
}
}
// charge et compile le code
load(text) {
// on supprime les commentaires
this.text = text.replaceAll(/(#|\/\/).*\n/g, "\n");
// on insère une fin de ligne si le texte n'en a pas à sa fin
if (this.text[this.text.length-1] != "\n") this.text = this.text+"\n";
if (! this.analyse()) {
alert("Erreur ligne "+this.lineNumber+" sur '" +this.yytext+"'");
return false;
}
return this.assemble();
}
pc = 0;
fp = 0;
stack;
step; // step number of the current run
maxSteps;
runCode(maxSteps, d) {
debug = d;
//for (let i = 0; i < this.codes.length; i++) console.log(i+ " "+ this.codes[i][0]);
this.pc = 0;
this.fp = 0;
this.stack = new Pile();
if (debug)
this.trace("Programme\n"+this.dumpProgram() + "\nTraces exécution\n"
+ " pc | Instruction | fp pile\n"
+ "=====+===============+=============================\n");
this.cleanInputAndErrorBoxes();
this.step = 0;
this.maxSteps = maxSteps;
this.resumeRun();
}
resumeRun(readCode, readResult) {
if (readCode != undefined && readResult != undefined) { // return from read
// READ or READF ?
if (readCode == "READ")
this.stack.push(readResult);
else if (readCode == "READF")
this.stack.pushFloat64(readResult);
else {
this.trace("Read Code inattendu "+readCode, true);
return;
}
this.step++;
}
for (; this.maxSteps <= 0 || this.step < this.maxSteps; this.step++) // while (true)
{
// if (this.pc < 0 || this.pc >= this.program.getSize()) break;
try {
if (debug) {
let s = this.pc.toString().padStart(4, " ")+" | ";
let junk;
[junk, s] = this.dumpInstruction(this.pc, s);
s += " |"+this.fp.toString().padStart(4,' ')+' '+this.stack.toString()+"\n";
this.trace(s);
}
if (this.codes[this.program.get(this.pc++)][2]() === true)
break;
} catch(e) {
this.trace(e, true);
break;
}
}
}
trace(s, error) {
if (error) {
console.error(s);
errorBox.innerText = s;
errorBox.hidden = false;
errorBox.scrollIntoView();
} else {
tracesBox.innerText += s;
console.log(s);
}
}
cleanInputAndErrorBoxes() {
errorBox.innerText = "";
errorBox.hidden = true;
inputBox.value = "";
inputBox.hidden = true;
}
read() {
inputBox.hidden = false;
inputBox.focus();
}
enterText(e) {
if (e.code == "Enter") {
let readResult = 0;
// What is the last op token
let t = this.codes[this.program.get(this.pc-1)][0];
switch (t) {
case "READ":
readResult = Number.parseInt(inputBox.value, 10);
if (! Number.isInteger(readResult)) {
alert(inputBox.value+" n'est pas un nombre entier");
return;
}
break;
case "READF":
readResult = Number.parseFloat(inputBox.value);
if (Number.isNaN(readResult)) {
alert(inputBox.value+" n'est pas un nombre entier");
return;
}
break;
default:
this.trace("Code inattendu pour lecture "+t+"\n", true);
return;
}
tracesBox.innerText += " > "+readResult+"\n";
inputBox.value = "";
inputBox.hidden = true;
console.log("READ ->"+readResult);
this.resumeRun(t, readResult);
}
}
}

View File

@@ -0,0 +1,153 @@
// Class Pile
// Auteur : Jacques Madelaine
// Une Pile est essentiellement une pile d'entiers ("int32")
// Elle peut aussi empiler et dépiler des flottants définis sur 64 bits.
// L'index est sur les entiers.
// Rappel : en JavaScript les Number sont tous stockés comme des flottants 64 bits.
// Il faut donc voir cette Pile comme un objet pédagogique
// et non une implémentation optimale en JavaScipt.
"use strict";
class Pile {
buffer; // ArrayBuffer de bytes
pile; // On va utiliser 4 bytes pour les entiers et 8 pour les flottants
haut = 0; // index de sommet de pile en bytes
// size : taille en bytes de la pile
constructor(size) {
if (size === undefined)
size = 1000;
this.buffer = new ArrayBuffer(size);
this.pile = new DataView(this.buffer);
}
push(i) {
if (! Number.isInteger(i))
throw new TypeError("Try to push a non integer");
this.pile.setInt32(this.haut, i);
this.haut += 4;
}
doPush(nb) { for(; nb > 0; nb--) this.push(0); }
pop() {
this.haut -= 4;
return this.pile.getInt32(this.haut);
}
doPop(nb) { for(; nb > 0; nb--) this.pop(); }
peek() {
return this.pile.getInt32(this.haut-4);
}
set(index, i) {
if (! Number.isInteger(i))
throw new TypeError("Try to set a non integer");
this.pile.setInt32(index*4, i);
}
get(index) {
return this.pile.getInt32(index*4);
}
pushFloat64(f) {
this.pile.setFloat64(this.haut, f);
this.haut += 8;
}
popFloat64() {
this.haut -= 8;
return this.pile.getFloat64(this.haut);
}
peekFloat64() {
return this.pile.getFloat64(this.haut-8);
}
setFloat64(index, f) {
this.pile.setFloat64(index*4, f);
}
getFloat64(index) {
return this.pile.getFloat64(index*4);
}
getMaxSize() {
return this.buffer.byteLength/2;
}
getSize() {
return this.haut/4;
}
toString() {
let out = "[ ";
for (let i = 0; i < this.getSize(); i++)
out += this.get(i) + ", ";
return out + " ] " + this.getSize();
}
}
/*
let p = new Pile();
try {
let d = 12.34;
let i = 0x7FFFFFFF;
let j = 0;
p.push(i);
j = p.peek();
console.log(i + " =?= " + j);
let N = 5;
for(let n = 0; n < N; n++) {
p.push(n);
console.log("push "+n+" " + n);
}
console.log("get(2) " + p.get(2));
console.log(p.toString());
for(let n = N; n > 0; n--) {
let m = p.pop();
// if (2*n != m)
console.log("push "+ n + " pop " + m);
}
p.pop();
p.push(40);
p.pushFloat64(d);
p.pushFloat64(13.34);
p.push(i);
console.log(p.toString());
for (let n = 0; n<6;n++)
console.log("p.getFloat64("+n+") = "+p.getFloat64(n));
p.pop();
p.popFloat64();
let e = p.popFloat64();
console.log(d + " =?= " + e);
d = -1;
p.pushFloat64(d);
e = p.popFloat64();
console.log(d + " =?= " + e);
//
// for (i = 0 ; i < 1000000; i++)
// p.push(i);
} catch(e) {
console.error(e);
}
*/

Binary file not shown.

View File

@@ -0,0 +1,72 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>MVàP</title>
<script src="js/Pile.js" type="text/javascript"></script>
<script src="js/MVaP.js" type="text/javascript"></script>
<style type="text/css">
body{ color: #333; font: 13px 'Lucida Grande', Verdana, sans-serif; }
</style>
</head>
<body>
<h1>Machine Virtuelle à Pile</h1>
<h2>Historique</h2>
Blabla sur la MVàP...
<h2>Documentation</h2>
Toute la doc
<p>
lorem ipsum
<h2>La MVàP</h2>
<h3>Entrez le code</h3>
<div class="runcode">
<textarea id="codeMVaP" name="Code MVàP" label="Enter your code" rows="15" cols="33" spellcheck="false">
PUSHI 2
DUP
PUSHI 3
ADD
PUSHI 4
MUL
WRITE
HALT
</textarea>
</div>
<h3>Exécution</h3>
<script>
var M = "";
function runcode() {
let text = document.getElementById("codeMVaP").value;
let d = document.getElementById("debug").checked;
let maxSteps = document.getElementById("MaxSteps").value;
if (M == "") M = new MVaP();
M.load(text);
M.runcode(maxSteps, d);
}
</script>
<input type="button" value="Run" onclick="runcode();"/>
<label for="MaxSteps">Nombre maximum de pas (O pour pas de limite)</label>
<input type="text" id="MaxSteps" name="MaxSteps" value="1000" minLength="4" maxLength="8" size="10"/>
<label for="debug">Traces pas à pas</label>
<input type="checkbox" id="debug" name="debug" checked />
<pre>
<input type="button" value="Nettoyer les traces" onclick="traces.innerText='';"/>
<div id="traces"></div>
</pre>
<script>
</script>
</body>
</html>

View File

@@ -0,0 +1,216 @@
/**
* Code Binaire pour Machine Virtuelle à Pile.
* Les flottants sont des double qui prennent la place de 2 ints.
* Les adresses sont des int.
* CALL empile l'adresse de retour et la vauler du framePointer dans la pile.
*/
public class CBaP {
pc = 0;
fp = 0;
stack;
program;
const SizeofDouble = 2;
_debug = false;
CBaP(program, debug, stackSize) {
this.program = program;
this._debug = debug;
this.stack = new Pile(stackSize);
}
dumpInstruction(ii, out) {
let i = ii;
switch(program.get_int(i)) {
case AssembleMVaP.ADD: out.print("ADD "); break;
case AssembleMVaP.SUB: out.print("SUB "); break;
case AssembleMVaP.MUL: out.print("MUL "); break;
case AssembleMVaP.DIV: out.print("DIV "); break;
case AssembleMVaP.INF: out.print("INF "); break;
case AssembleMVaP.INFEQ: out.print("INFEQ "); break;
case AssembleMVaP.SUP: out.print("SUP "); break;
case AssembleMVaP.SUPEQ: out.print("SUPEQ "); break;
case AssembleMVaP.EQUAL: out.print("EQUAL "); break;
case AssembleMVaP.NEQ: out.print("NEQ "); break;
case AssembleMVaP.FADD: out.print("FADD "); break;
case AssembleMVaP.FSUB: out.print("FSUB "); break;
case AssembleMVaP.FMUL: out.print("FMUL "); break;
case AssembleMVaP.FDIV: out.print("FDIV "); break;
case AssembleMVaP.FINF: out.print("FINF "); break;
case AssembleMVaP.FINFEQ: out.print("FINFEQ"); break;
case AssembleMVaP.FSUP: out.print("FSUP "); break;
case AssembleMVaP.FSUPEQ: out.print("FSUPEQ"); break;
case AssembleMVaP.FEQUAL: out.print("FEQUAL"); break;
case AssembleMVaP.FNEQ: out.print("FNEQ "); break;
case AssembleMVaP.ITOF: out.print("ITOF "); break;
case AssembleMVaP.FTOI: out.print("FTOI "); break;
case AssembleMVaP.RETURN: out.print("RETURN"); break;
case AssembleMVaP.POP: out.print("POP "); break;
case AssembleMVaP.READ: out.print("READ "); break;
case AssembleMVaP.WRITE: out.print("WRITE "); break;
case AssembleMVaP.WRITEF: out.print("WRITEF"); break;
case AssembleMVaP.PADD: out.print("PADD "); break;
case AssembleMVaP.PUSHGP: out.print("PUSHGP"); break;
case AssembleMVaP.PUSHFP: out.print("PUSHFP"); break;
case AssembleMVaP.DUP: out.print("DUP "); break;
case AssembleMVaP.PUSHI: out.print("PUSHI "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHG: out.print("PUSHG "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STOREG: out.print("STOREG"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHL: out.print("PUSHL "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STOREL: out.print("STOREL"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHR: out.print("PUSHR "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.STORER: out.print("STORER"); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.FREE: out.print("FREE "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.ALLOC: out.print("ALLOC "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.PUSHF: out.print("PUSHF "); ++i; out.format("%7.3f ", program.get_double(++i)); break;
case AssembleMVaP.JUMP: out.print("JUMP "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.JUMPF: out.print("JUMPF "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.JUMPI: out.print("JUMPI "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.CALL: out.print("CALL "); out.format("%7d ", program.get_int(++i)); break;
case AssembleMVaP.HALT: out.print("HALT "); break;
default: System.err.println("Code inconnu "+program.get_int(i)+" ligne "+i);
}
if (i == ii) out.print(" ");
return i;
}
public void dumpProgram(PrintStream out) {
// out.println(program.toString(true));
// out.println(program.toString());
out.println(" Adr | Instruction");
out.println("-----+---------------");
for(int i = 0; i < program.getSize(); i++) {
out.format("%4d | ", i);
i = dumpInstruction(i, out);
out.println();
}
}
private void _call(int label) {
push(pc+1);
push(fp);
fp = size();
pc = label;
}
private void _return() {
while(size() > fp)
pop();
fp = pop();
pc = pop();
}
private BufferedReader input = null;
private int _read() {
try {
if (input == null)
input = new BufferedReader(new InputStreamReader(System.in));
return Integer.parseInt(input.readLine());
} catch (Exception e) {
System.err.println(e);
System.exit(1);
return 0;
}
}
public boolean execute() {
if (_debug) {
dumpProgram(System.out);
System.err.println();
System.err.println(" pc | | fp pile");
System.err.println("====================================================");
}
pc = 0;
while (true) {
if (_debug) {
System.err.format("%4d | ", pc);
dumpInstruction(pc, System.err);
System.err.format("| %4d %s\n", fp, stack.toString());
}
try {
int p1, p2; // utile pour être sûr de l'ordre des pop() !
double d1, d2;
switch(program.get_int(pc++)) {
case AssembleMVaP.ADD: p1 = pop(); p2 = pop(); push(p2 + p1); break;
case AssembleMVaP.SUB: p1 = pop(); p2 = pop(); push(p2 - p1); break;
case AssembleMVaP.MUL: p1 = pop(); p2 = pop(); push(p2 * p1); break;
case AssembleMVaP.DIV: p1 = pop(); p2 = pop(); push(p2 / p1); break;
case AssembleMVaP.INF: p1 = pop(); p2 = pop(); push(p2 < p1 ? 1 : 0); break;
case AssembleMVaP.INFEQ: p1 = pop(); p2 = pop(); push(p2 <= p1 ? 1 : 0); break;
case AssembleMVaP.SUP: p1 = pop(); p2 = pop(); push(p2 > p1 ? 1 : 0); break;
case AssembleMVaP.SUPEQ: p1 = pop(); p2 = pop(); push(p2 >= p1 ? 1 : 0); break;
case AssembleMVaP.EQUAL: p1 = pop(); p2 = pop(); push(p2 == p1 ? 1 : 0); break;
case AssembleMVaP.NEQ: p1 = pop(); p2 = pop(); push(p2 != p1 ? 1 : 0); break;
case AssembleMVaP.FADD: d1 = pop_double(); d2 = pop_double(); push(d2 + d1); break;
case AssembleMVaP.FSUB: d1 = pop_double(); d2 = pop_double(); push(d2 - d1); break;
case AssembleMVaP.FMUL: d1 = pop_double(); d2 = pop_double(); push(d2 * d1); break;
case AssembleMVaP.FDIV: d1 = pop_double(); d2 = pop_double(); push(d2 / d1); break;
case AssembleMVaP.FINF: d1 = pop_double(); d2 = pop_double(); push(d2 < d1 ? 1 : 0); break;
case AssembleMVaP.FINFEQ: d1 = pop_double(); d2 = pop_double(); push(d2 <= d1 ? 1 : 0); break;
case AssembleMVaP.FSUP: d1 = pop_double(); d2 = pop_double(); push(d2 > d1 ? 1 : 0); break;
case AssembleMVaP.FSUPEQ: d1 = pop_double(); d2 = pop_double(); push(d2 >= d1 ? 1 : 0); break;
case AssembleMVaP.FEQUAL: d1 = pop_double(); d2 = pop_double(); push(d2 == d1 ? 1 : 0); break;
case AssembleMVaP.FNEQ: d1 = pop_double(); d2 = pop_double(); push(d2 != d1 ? 1 : 0); break;
case AssembleMVaP.FTOI: push((int)pop_double()); break;
case AssembleMVaP.ITOF: push((double)pop()); break;
case AssembleMVaP.RETURN: _return(); break;
case AssembleMVaP.POP: pop(); break;
case AssembleMVaP.READ: push(_read()); break;
case AssembleMVaP.WRITE: System.out.format("%7d\n", peek()); break;
case AssembleMVaP.WRITEF: System.out.format("%7.3f\n", peek_double()); break;
case AssembleMVaP.PADD: p1 = pop(); p2 = pop(); push(p2 + p1); break;
case AssembleMVaP.PUSHGP: push(0); break;
case AssembleMVaP.PUSHFP: push(fp); break;
case AssembleMVaP.DUP: push(peek()); break;
case AssembleMVaP.PUSHI: push(program.get_int(pc++)); break;
case AssembleMVaP.PUSHG: push(get(program.get_int(pc++))); break;
case AssembleMVaP.STOREG: set(program.get_int(pc++), pop()); break;
// ajouter -2 si adresse négative ?
case AssembleMVaP.PUSHL: push(get(fp+program.get_int(pc++))); break;
case AssembleMVaP.STOREL:set(fp+program.get_int(pc++), pop()); break;
case AssembleMVaP.PUSHR: push(get(pop()+program.get_int(pc++)));break;
case AssembleMVaP.STORER:p1 = pop(); p2 = pop(); set(p2+program.get_int(pc++), p1); break;
case AssembleMVaP.FREE: doPop(program.get_int(pc++)); break;
case AssembleMVaP.ALLOC: doPush(program.get_int(pc++)); break;
case AssembleMVaP.PUSHF: push(program.get_int(pc++)); push(program.get_int(pc++)); break;
case AssembleMVaP.JUMP: pc = program.get_int(pc); break;
case AssembleMVaP.JUMPF: if (pop() == 0) pc = program.get_int(pc); else pc++; break;
case AssembleMVaP.JUMPI: pc = program.get_int(pc)+pop(); break;
case AssembleMVaP.CALL: _call(program.get_int(pc)); break;
case AssembleMVaP.HALT: return true;
default: System.err.println("Code inconnu "+program.get_int(pc)+" ligne "+pc); return false;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println(stack.getException());
return false;
}
}
}
void push(int i) { stack.push(i); }
int pop() { return stack.pop_int(); }
void push(double d) { stack.push(d); }
double pop_double() { return stack.pop_double(); }
int peek() { return stack.peek_int(); }
double peek_double() { return stack.peek_double(); }
int size() { return stack.getSize(); }
void set(int index, int i) { stack.set(index, i); }
int get(int index) { return stack.get_int(index); }
void doPop(int nb) { for(; nb > 0; nb--) stack.pop_int(); }
void doPush(int nb) { for(; nb > 0; nb--) stack.push(0); }
}

View File

@@ -0,0 +1,33 @@
digraph LexerMVaP {
Start -> Start [label="space "];
Start -> Id [label="[A-Z]"];
Id -> Id [label="[A-Z]"];
Start -> Int [label="[-+0-9]"];
Int -> Int [label="[0-9]"];
Int -> Decimal [label="."];
Int -> E [label="E"];
Decimal -> Decimal [label="[0-9]"];
Decimal -> E [label="E"];
E -> Exponent [label="[-+0-9]"];
Exponent -> Exponent [label="[0-9]"];
Start -> error [label="*"];
Id -> error [label="*"];
Int -> error [label="*"];
Decimal -> error [label="*"];
E -> error [label="*"];
Exponent -> error [label="*"];
edge [color=red, fontcolor=red];
Start -> Start [label="NL/[Newline] "];
Id -> Start [label="space/ID"];
Int -> Start [label="space/Int"];
Decimal -> Start [label="space/Float"];
Exponent -> Start [label="space/Float"];
{ rank = same; Decimal E Exponent }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -0,0 +1,374 @@
// MVàP
let debugLexer = false;
let debug = true;
/** token lexicaux */
const T_ID = 0;
const T_INT = 1;
const T_FLOAT = 2;
const T_SPACE = 3;
const T_NL = 4;
const T_ERROR = 5;
const T_EOF = 6;
T_Text = [ "T_ID", "T_INT", "T_FLOAT", "T_SPACE", "T_NL", "T_ERROR", "T_EOF" ];
/** token grammaticaux */
const INSTR1 = 0;
const INSTR2 = 1;
const INT = 2;
const INSTR2F = 3;
const FLOAT = 4;
const LABEL = 5;
const JUMP = 6;
const NEWLINE = 7;
class MVaP {
codes = [ // pour l'interprète de MVàP
[ "ADD", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2+p1); } ],
[ "SUB", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2-p1); } ],
[ "MUL", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2*p1); } ],
[ "DIV", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(Math.floor(p2/p1)); } ],
[ "INF", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2<p1?1:0); } ],
[ "INFEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2<=p1?1:0); } ],
[ "SUP", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2>p1?1:0); } ],
[ "SUPEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2>=p1?1:0); } ],
[ "EQUAL", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2==p1?1:0); } ],
[ "NEQ", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2!=p1?1:0); } ],
[ "FADD", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2+d1); } ],
[ "FSUB", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2-d1); } ],
[ "FMUL", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2*d1); } ],
[ "FDIV", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.pushFloat64(d2/d1); } ],
[ "FINF", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2<d1?1:0); } ],
[ "FINFEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2<=d1?1:0); } ],
[ "FSUP", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2>d1?1:0); } ],
[ "FSUPEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2>=d1?1:0); } ],
[ "FEQUAL", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2==d1?1:0); } ],
[ "FNEQ", INSTR1, () => { let d1 = this.stack.popFloat64(); let d2 = this.stack.popFloat64(); this.stack.push(d2!=d1?1:0); } ],
[ "ITOF", INSTR1, () => { let p1 = this.stack.pop(); this.trace("ITOF "+p1); this.stack.pushFloat64(p1); } ],
[ "FTOI", INSTR1, () => { this.stack.push(Math.round(this.stack.popFloat64())); } ],
[ "RETURN", INSTR1, () => { while(this.stack.getSize() > this.fp) this.stack.pop();
this.fp = this.stack.pop(); this.pc = this.stack.pop(); } ],
[ "POP", INSTR1, () => { this.stack.pop(); } ],
[ "READ", INSTR1, () => { this.stack.push(0); } ], // on lit toujours 0 (idée : prendre le contenu d'un input de type text spécial
[ "WRITE", INSTR1, () => { this.trace(this.stack.peek()+"\n"); } ],
[ "WRITEF", INSTR1, () => { this.trace(this.stack.peekFloat64()+"\n"); } ],
[ "PADD", INSTR1, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.push(p2+p1); } ],
[ "PUSHGP", INSTR1, () => { this.stack.push(0); } ],
[ "PUSHFP", INSTR1, () => { this.stack.push(this.fp); } ],
[ "DUP", INSTR1, () => { this.stack.push(this.stack.peek()); } ],
[ "PUSHI", INSTR2, () => { this.stack.push(this.program.get(this.pc++)); } ],
[ "PUSHG", INSTR2, () => { this.stack.push(this.stack.get(this.program.get(this.pc++)));} ],
[ "STOREG", INSTR2, () => { this.stack.set(this.program.get(this.pc++), this.stack.pop())} ],
[ "PUSHL", INSTR2, () => { this.stack.push(this.stack.get(this.fp+this.program.get(this.pc++))); } ],
[ "STOREL", INSTR2, () => { this.stack.set(this.fp+this.program.get(this.pc++), this.stack.pop()); } ],
[ "PUSHR", INSTR2, () => { this.stack.push(this.stack.get(this.stack.pop()+this.program.get(this.pc++))); } ],
[ "STORER", INSTR2, () => { let p1 = this.stack.pop(); let p2 = this.stack.pop(); this.stack.set(p2+this.program.get(this.pc++)); } ],
[ "FREE", INSTR2, () => { this.stack.doPop(this.program.get(this.pc++)); } ],
[ "ALLOC", INSTR2, () => { this.stack.doPush(this.program.get(this.pc++)); } ],
[ "PUSHF", INSTR2F, () => { this.stack.pushFloat64(this.program.getFloat64(this.pc)); this.pc += 2; } ],
[ "CALL", JUMP, () => { this.stack.push(this.pc+1); this.stack.push(this.fp);
this.fp = this.stack.getSize();
this.pc = this.program.get(this.pc); } ],
[ "JUMP", JUMP, () => { this.pc = this.program.get(this.pc); } ],
[ "JUMPF", JUMP, () => { if (this.stack.pop()==0) this.pc = this.program.get(this.pc);
else this.pc++; } ],
[ "JUMPI", JUMP, () => { this.pc = this.program.get(this.pc)+this.stack.pop();} ],
[ "HALT", INSTR1, () => { return true; } ],
[ "LABEL", LABEL, /* erreur interne */ ]
];
/*** Ajouter instr : READF WRITEF POPF */
tokens = []; // indexe le tableau codes sur son premier élément
text = "";
// <Lexer>
// see LexerMVaP.png for the DFA diagram
state = "Start";
text; // le texte à analyser
index = 0; // index dans la chaîne text
lineNumber = 1;
transitions = [
{ state: "Start", next: "Start", test: (c) => { return c == "\n"; }, token: T_NL },
{ state: "Start", next: "Start", test: (c) => { return c.trim() == ""; }, token: false },
{ state: "Start", next: "Id", test: (c) => { return "A" <= c && c <= "Z"; }, token: false },
{ state: "Id", next: "Id", test: (c) => { return "A" <= c && c <= "Z"; }, token: false},
{ state: "Id", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_ID},
{ state: "Start", next: "Int", test: (c) => { return ("0" <= c && c <= "9")||c=="-"||c=="+"; }, token: false },
{ state: "Int", next: "Int", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Int", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_INT },
{ state: "Int", next: "Decimal", test: (c) => { return c == "."; }, token: false },
{ state: "Int", next: "E", test: (c) => { return c == "E"; }, token: false },
{ state: "Decimal", next: "Decimal", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Decimal", next: "E", test: (c) => { return c == "E"; }, token: false },
{ state: "Decimal", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_FLOAT },
{ state: "E", next: "Exponent", test: (c) => { return ("0"<=c && c<="9")||c=="-"||c=="+"; }, token: false },
{ state: "Exponent", next: "Exponent", test: (c) => { return "0" <= c && c <= "9"; }, token: false },
{ state: "Exponent", next: "Start", test: (c) => { return c.trim() == ""; }, token: T_FLOAT }
// else: error
];
initLexer() {
this.index = 0;
this.lineNumber = 1
console.log("*****************\n* C'est parti *\n*****************");
}
// Donne la transition correspondante à l'état courant et au caractère entrant
transition(c) {
for (let v of this.transitions.values()) {
// on aurait pu indexer les transitions sur state au lieu de tout énumérer
if (this.state == v.state && v.test(c)) {
return v;
}
}
return T_ERROR;
}
yytext = ""; // contient le texte du jeton syntaxique (nommé comme dans lex/yacc)
nextToken() {
let c;
let trans;
while (true) {
c = this.text[this.index];
if (c === undefined)
return T_EOF;
c = c.toUpperCase();
trans = this.transition(c);
if (trans == T_ERROR) { this.yytext += c; return T_ERROR; }
if (this.state != trans.next && this.state == "Start") this.yytext ="";
this.state = trans.next;
this.index++;
if (Number.isInteger(trans.token)) {
break;
}
this.yytext += c;
}
// séparateur significatif
if (c == "\n") {
if (trans.token != T_NL) {
this.index--; // ne pas manger la fin de ligne non prise en compte
// une autre façon de dire est que l'on n'incrémente pas l'index sur le renvoi de token
// sauf en cas de token constitué de séparateur
} else
this.lineNumber++; // on en profite pour compter la ligne
}
// console.log(" line " + this.lineNumber + " " + trans.token + " '" + yytex + "' i="+i);
return trans.token;
}
// </Lexer>
// <Analyse sémantique et assemblage>
/** Grammaire des instructions
instr
: INSTR1 T_NL
| INSTR2 T_INT T_NL
| JUMP T_INT T_NL
| INSTR2F T_FLOAT T_NL
| LABEL T_NL
| T_NL
;
*/
program;// Pile contenant le programme compilé
adressesLabels; // adresse des étiquettes pour l'assemblage
analyse() {
this.initLexer();
let token; // le jeton lexical courant
let state = NEWLINE; // état de l'analyseur grammatical (ici le précédent syntagme grammatical)
this.program = new Pile();
this.adressesLabels = [];
while (true) {
this.yytext = "";
token = this.nextToken();
if (debugLexer)
console.log("** nextToken line "+this.lineNumber+" token "+token+" "+T_Text[token]+" '" +this.yytext+"' index "+this.index+" state "+state);
switch(token) {
case T_EOF:
return true;
case T_ID:
let k = this.tokens[this.yytext];
if (k === undefined || state != NEWLINE) return false;
state = this.codes[k][1];
if (state != LABEL) { // pas d'instruction LABEL dans le code
this.program.push(k);
}
break;
case T_INT:
if (state == LABEL) { // on mémorise juste l'adresse pour la passe d'assemblage
this.adressesLabels[Number(this.yytext)] = this.program.getSize(); // ± 1 who knows?
} else if(state == INSTR2 || state == JUMP) {
this.program.push(Number(this.yytext));
} else return false;
state = INT;
break;
case T_FLOAT:
if (state != INSTR2F) return false;
state = FLOAT;
this.program.pushFloat64(Number(this.yytext));
break;
case T_NL:
if (state == INSTR2 || state == JUMP || state == INSTR2F) return false;
state = NEWLINE;
break;
case T_ERROR:
return false;
default:
alert("Erreur interne prévenir la maintenance d'urgence");
return false;
}
}
console.log("Programme correct syntaxiquement");
return true;
}
assemble() {
// change les numéros d'étiquettes par les adresses des instructions pointées.
console.log("Label "+this.adressesLabels);
console.log(this.dumpProgram());
for (let i = 0; i < this.program.getSize(); i++) {
let k = this.program.get(i);
// Attention il faut compter si par malchance un entier = code d'un saut
switch (this.codes[k][1]) {
case INSTR1:
break;
case INSTR2:
i++; // on saute l'entier qui suit
break;
case INSTR2F:
i += 2; // on saute le flottant qui suit
break;
case JUMP: // met à jour l'adresse du saut
i++;
console.log("--- set "+i);
this.program.set(i, this.adressesLabels[this.program.get(i)]);
break;
default:
alert("Erreur assemblage");
}
}
// console.log(this.dumpProgram());
}
// </Analyse sémantique et assemblage>
dumpInstruction(i, result) {
let code = this.codes[this.program.get(i)];
result += code[0].padEnd(6, ' ');
switch (code[1]) {
case INSTR2:
case JUMP:
result += this.program.get(++i).toString().padStart(7, ' ');
break;
case INSTR2F:
result += this.program.getFloat64(++i).toString().padStart(7, ' ');
i++;
break;
default:
result += " ";
}
return [i, result];
}
dumpProgram() {
let result = "";
result += this.program + "\n";
result += " Adr | Instruction\n-----+---------------\n";
for(let i = 0; i < this.program.getSize(); i++) {
result += i.toString().padStart(4, " ")+" | ";
[i, result] = this.dumpInstruction(i, result);
result += "\n";
}
return result;
}
constructor() {
// init tokens
for (let k of this.codes.keys())
this.tokens[this.codes[k][0]] = k;
}
// charge et compile le code
load(text) {
this.text = text;
// on insère une fin de ligne si le texte n'en a pas à sa fin
if (text[text.length-1] != "\n") this.text = text+"\n";
if (! this.analyse()) {
alert("Erreur ligne "+this.lineNumber+" sur '" +this.yytext+"'");
return;
}
this.assemble();
}
pc = 0;
fp = 0;
stack;
runcode(maxSteps, d) {
debug = d;
//for (let i = 0; i < this.codes.length; i++) console.log(i+ " "+ this.codes[i][0]);
this.pc = 0;
this.fp = 0;
this.stack = new Pile();
try {
if (debug)
this.trace("Programme\n"+this.dumpProgram() + "\nTraces exécution\n"
+ " pc | Instruction | fp pile\n"
+ "=====+===============+=============================\n");
for (let i= 0; i < maxSteps; i++) // while (true)
{
if (this.pc < 0 || this.pc >= this.program.getSize())
break;
if (debug) {
let s = this.pc.toString().padStart(4, " ")+" | ";
let junk;
[junk, s] = this.dumpInstruction(this.pc, s);
s += " |"+this.fp.toString().padStart(4,' ')+' '+this.stack.toString()+"\n";
this.trace(s);
}
if (this.codes[this.program.get(this.pc++)][2]() === true)
break;
}
} catch(e) {
this.trace(e);
console.error(e);
}
}
trace(s) {
traces.innerText += s;
console.log(s);
}
}

View File

@@ -0,0 +1,153 @@
// Class Pile
// Auteur : Jacques Madelaine
// Une Pile est essentiellement une pile d'entiers ("int32")
// Elle peut aussi empiler et dépiler des flottants définis sur 64 bits.
// L'index est sur les entiers.
// Rappel : en JavaScript les Number sont tous stockés comme des flottants 64 bits.
// Il faut donc voir cette Pile comme un objet pédagogique
// et non une implémentation optimale en JavaScipt.
"use strict";
class Pile {
buffer; // ArrayBuffer de bytes
pile; // On va utiliser 4 bytes pour les entiers et 8 pour les flottants
haut = 0; // index de sommet de pile en bytes
// size : taille en bytes de la pile
constructor(size) {
if (size === undefined)
size = 1000;
this.buffer = new ArrayBuffer(size);
this.pile = new DataView(this.buffer);
}
push(i) {
if (! Number.isInteger(i))
throw new TypeError("Try to push a non integer");
this.pile.setInt32(this.haut, i);
this.haut += 4;
}
doPush(nb) { for(; nb > 0; nb--) this.push(0); }
pop() {
this.haut -= 4;
return this.pile.getInt32(this.haut);
}
doPop(nb) { for(; nb > 0; nb--) this.pop(); }
peek() {
return this.pile.getInt32(this.haut-4);
}
set(index, i) {
if (! Number.isInteger(i))
throw new TypeError("Try to set a non integer");
this.pile.setInt32(index*4, i);
}
get(index) {
return this.pile.getInt32(index*4);
}
pushFloat64(f) {
this.pile.setFloat64(this.haut, f);
this.haut += 8;
}
popFloat64() {
this.haut -= 8;
return this.pile.getFloat64(this.haut);
}
peekFloat64() {
return this.pile.getFloat64(this.haut-8);
}
setFloat64(index, f) {
this.pile.setFloat64(index*4, f);
}
getFloat64(index) {
return this.pile.getFloat64(index*4);
}
getMaxSize() {
return this.buffer.byteLength/2;
}
getSize() {
return this.haut/4;
}
toString() {
let out = "[ ";
for (let i = 0; i < this.getSize(); i++)
out += this.get(i) + ", ";
return out + " ] " + this.getSize();
}
}
/*
let p = new Pile();
try {
let d = 12.34;
let i = 0x7FFFFFFF;
let j = 0;
p.push(i);
j = p.peek();
console.log(i + " =?= " + j);
let N = 5;
for(let n = 0; n < N; n++) {
p.push(n);
console.log("push "+n+" " + n);
}
console.log("get(2) " + p.get(2));
console.log(p.toString());
for(let n = N; n > 0; n--) {
let m = p.pop();
// if (2*n != m)
console.log("push "+ n + " pop " + m);
}
p.pop();
p.push(40);
p.pushFloat64(d);
p.pushFloat64(13.34);
p.push(i);
console.log(p.toString());
for (let n = 0; n<6;n++)
console.log("p.getFloat64("+n+") = "+p.getFloat64(n));
p.pop();
p.popFloat64();
let e = p.popFloat64();
console.log(d + " =?= " + e);
d = -1;
p.pushFloat64(d);
e = p.popFloat64();
console.log(d + " =?= " + e);
//
// for (i = 0 ; i < 1000000; i++)
// p.push(i);
} catch(e) {
console.error(e);
}
*/

Binary file not shown.

View File

@@ -0,0 +1,7 @@
# calcule : 2 + 40
PUSHI 2
PUSHI 40
ADD
WRITE
POP
HALT

Binary file not shown.

View File

@@ -0,0 +1,27 @@
# On prend au départ 2
# on ajoute 3 et on élève le tout au carré
# on recommence et on s'arrête dès que l'on dépasse 100
PUSHI 2
LABEL 1
PUSHI 3
ADD
WRITE
CALL 2
WRITE
# a-t-on un sommet de pile < 100 ?
DUP
PUSHI 100
SUP
JUMPF 1
# le nombre affiché est plus grand que 100, on s'arrête
HALT
# procédure qui élève un nombre au carré
LABEL 2
# récupère le premier paramètre (-3 = -1 - 2)
PUSHL -3
DUP
MUL
# on a donc son carré en sommet de pile
STOREL -3
# on remplace la valeur
RETURN