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