Pour ma façon de faire chaque fonction (représentant une règle de production) reçoit un arbre (AST) auquel elle doit attacher le sous arbre produit. Lit permet d'obtenir le prochain jeton. Pour l'expression:
// [OP_UNAIRE] (CTE_ENTIERE | SYMBOLE [TUPLE] | TUPLE) [OP_BINAIRE EXPR]
static void SOUS_EXPR(AST &expr) {
AST operande;
AST *courant = &operande;
if (opUnaire())
// On attachera le reste à cet opérateur
courant = &courant->attach(ES_OP_UNAIRE, lit());
if (prochain(CTE_NOMBRE))
courant->attach(ES_JETON, lit());
else if (prochain(IDENTIFICATEUR)) {
SYMBOLE(*courant);
if (prochain(PAR_OUVRANTE))
TUPLE(*courant);
}
else if (prochain(PAR_OUVRANTE)) {
AST tuple;
TUPLE(tuple);
// Tuple à un seul élément = expression
// FIXME: optimisation haut niveau de l'arbre?
if (tuple.fils.size() == 1) {
tuple.removeRoot(); // <TUPLE>
tuple.removeRoot(); // <EXPRESSION>
}
courant->attach(tuple);
}
else
erreur("expression attendue");
// Opérateur binaire + autre expr
if (opBinaire()) {
AST operation(ES_OP_BINAIRE, lit()), operande2;
SOUS_EXPR(operande2);
operation.attach(operande);
operation.attach(operande2);
expr.attach(operation);
}
else
expr.attach(operande);
}
static void EXPR(AST &arbre) {
AST expr(ES_EXPRESSION);
SOUS_EXPR(expr);
arbre.attach(expr);
}
Exemple pour a=0+(1*-2)-3;
<PROG>
<BLOC_INSTRUCTION>
<AFFECTATION>
<SYMBOLE {IDENTIFICATEUR 'a'}>
<EXPRESSION>
<OP_BINAIRE {OP_PLUS '+'}>
<JETON {CTE_NOMBRE '0'}>
<OP_BINAIRE {OP_MOINS '-'}>
<OP_BINAIRE {OP_FOIS '*'}>
<JETON {CTE_NOMBRE '1'}>
<OP_UNAIRE {OP_MOINS '-'}>
<JETON {CTE_NOMBRE '2'}>
<JETON {CTE_NOMBRE '3'}>

En fait ce n'est pas exactement du RPN, mais une notation hiérarchique. Par exemple 1 + 2 est noté (+(1, 2)), 1+2+3 est noté (+(+(1, 2), 3), et ainsi de suite. Cela me facilitera certainement la vérification des types et du nombre d'arguments, ainsi que la cohérence globale (par exemple si j'écris 1*, cela ne crée pas d'erreur, par contre il est clair que la multiplication a besoin de plus d'un argument).
)
(disons moins difficile ^^)
.section text main: MOV r0, 0 boucle: ; r0 = 0 .. 10 ADD r0, 1 ; r0 = r0 + 1 SKIPEQ r0, 10, 1 ; if (r0 == 10) pc = pc + 1; (sortie de la boucle) GOTO boucle
0000 MOV r0, 0 0002 MOV r12, 1 0004 ADD r0, r12 0006 MOV r12, 10 0008 SKIPEQ r0, r12, 1 000a BRA -5 ; retour à MOV r12, 1
quel merde en fait d'avoir fait un processeur RISC, sans un tas de pseudo instructions tu fais pas grand chose

:a BRA :b MOV r0, 0 BNE r0, 0, :a :b NOP
0000 BRA 3 ; @0008 0002 MOV r0, 0 0004 MOV at, 0 0006 BNE r0, at, -4 ; @0000 0008 NOP
0000 BRA 5 ; @000c 0002 MOV r0, 255 (-1) 0004 EXT r0 0006 MOV at, 0 0008 BEQ r0, at, 1 ; @000a 000a BRA -6 ; @0000 000c NOP

Brunni (./38) :
Déjà il y a pour 14 bits d'opérande (sur 16), donc seulement 3 pour l'offset de branchement


BRA :a ; goto 'a' (adresse 0) :a ; label 'a' (adresse 2)
Et je n'arrive pas non plus à prouver qu'une optimisation (saut/chargement sur moins d'instructions) génèrera un code plus petit dans tous les cas (i.e. il n'y a jamais de répercussions).lbl_1: bra lbl_2 ... bra lbl1 lbl2:
)
Brunni (./45) :
Mais ta méthode nécessite elle aussi de passer n fois la liste des instructions en revue n'est-ce pas?


Folco (./40) :Brunni (./38) :
Déjà il y a pour 14 bits d'opérande (sur 16), donc seulement 3 pour l'offset de branchement
Ben je comprends que ton proc ait des problèmes d'orthogonalité.
$main mov r0, :irqHandler st r0, IRQHANDLER mov r0, (IRQ_HBLANK | IRQ_MASTER) st r0, IRQE b -1 ; boucle infinie :irqHandler push r0 ld8 r0, DISPSTAT ; scanline st r0, PALETTE pop r0 ReturnFromIrq
Kevin Kofler (./48) :
GCC utilisait Bison pour le C jusqu'à la version 4.0 et pour le C++ jusqu'à la version 3.3. Donc ça existe tout à fait, de vrais compilateurs utilisant un générateur de parseurs. Cela dit, depuis g++ 3.4 et depuis GCC 4.1, respectivement, le parseur est écrit à la main.

godzil@Maya ~ $ find . | grep -e "\.y$" ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/gengtype-yacc.y ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/treelang/parse.y ./Code/gcc4ti-pool/g/gcc-4.1.2/intl/plural.y


godzil@Maya ~ $ find . | grep -e "\.l$" ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/gengtype-lex.l ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/treelang/lex.l
Brunni (./50) :
./46 Cool, je pensais pas qu'on pouvait faire si simple. J'ai dû en faire pour Bison dans le cadre du cours (le 3ème labo voulait qu'on refasse le lexer et la grammaire avec Lex + Bison) elle ne gère pas la priorité et l'associativité correctement ^^
Godzil (./51) :
Bizzare je me demande que font des fichier .y dans gcc 4.1.2
./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/gengtype-yacc.y
./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/treelang/parse.y
./Code/gcc4ti-pool/g/gcc-4.1.2/intl/plural.y
Il y a meme du (f)lexgodzil@Maya ~ $ find . | grep -e "\.l$" ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/gengtype-lex.l ./Code/gcc4ti-pool/g/gcc-4.1.2/gcc/treelang/lex.l
SebRmv (./46) :
Sinon, notez que les "vrais" compilateurs n'utilisent en général pas les outils automatiques style yacc.



Brunni (./34) :
En parallèle je suis en train de songer à la création d'un petit processeur virtuel pour lequel je vais générer mon code. Ce qui semble le plus immédiat c'est bien sûr un processeur à pile, mais c'est pas très réaliste (et puis ça bypassera l'allocation des registres et tout). Donc je pense que je vais plutôt partir sur un processeur RISC 16-bits à architecture Harvard, afin de pouvoir avoir 64k de code et 16k de données + 16k paginés. Je complèterai avec 8k de RAM, 16k de VRAM (ben oui on va en faire une console), 4k de SRAM et 4k d'IO. Miam, ça va être l'occasion de mêler mon intérêt pour l'émulation avec celui de la compilation ^^
(mais après, il a été implémenté en FPGA, c'est encore plus mieux
)
)