// [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); }
<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'}>
.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)
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 ^^