MVaP en javascript

This commit is contained in:
Florent Madelaine 2024-01-22 09:02:25 +01:00
parent 460b23f88e
commit 9d2a57e7bd
6 changed files with 848 additions and 0 deletions

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);
}
*/