1

yop,

C'est pas un secret, je code un assembleur on-calc sur 89/92. J'en suis à faire le parseur de source, qui crée en une passe les fichiers object (un par source... normal ^^). Le linker viendra après.

J'y arrive, mais j'ai vraiment l'impression d'écrire un algo lourd, qui se traduit par un code volumineux, et répétitif pour les différents mots que je peux rencontrer (macro, commandes [.xdef, .include, .word etc...], instructions, labels etc...).
En gros, quand je tombe sur un ".xdef", je regarde si le mot qui vient après peut être un label valide, du point de vue de sa composition : d'abord une lettre, un underscore ou un antislash, puis une combinaison de lettres, underscores et points. Ca m'oblige, pour chaque fonction rencontrée, à réécrire la détection des lettres etc..., même si j'ai quelques fonctions (CheckAlpha, CheckNum etc...).

N'y a-t-il pas une approche radicalement différente, plus efficace en termes de clarté d'algorithme et de concision du code ? Parce que bonsoir le débogage d'un parseur tel que je suis en train de l'écrire. Et encore, je n'ai pas attaqué le décodage des instructions (genre move.l #ABCD,(%a0)+), ça va donner par la suite.

Il y a des algos spécialisés dans ce domaine j'imagine ? Ca doit être connu et archi-connu j'imagine, mais je ne sais pas quel mots chercher pour bûcher sur ce type d'algo. Bien sûr, vos avis persos sont les bienvenus.

Merci d'avance. smile

2

c'est normal que ce soit long et repetitif. ce que tu fais c une state machine, donc, faut tout couvrir (basiquement). c pour ca aussi qu'on utilise des generateurs de parseurs depuis une grammaire.

3

Ah ? C'est donc normal ? Je me demandais s'il n'avait pas une méthode, qui plutôt que de dire :
"j'ai telle commande, donc je dois trouver un label, je vérifie que ce qui est après en est un"
consisterait à penser plutôt :
"tiens, qu'est-ce que c'est que ce qui suit ? Ah, c'est un label, voyons si ma commande précédente peut en faire quelque chose".

Quelle est la bonne approche ?

Quant à une grammaire, je ne vois pas ce que c'est que ce concept, c'est peut-être ça qui me manque ?

4

Oui et non. Tu peux le faire, mais c'est grave lourd à implanter. En fait, ce que tu veux faire c'est reconnaître un langage, plus ou moins.

Chaque langage de programmation est régi par une grammaire, c'est-à-dire qu'elle suit une syntaxe rigoureuse. Ça tu l'as vu. À partir de cette syntaxe, tu peux déduire des règles qui reviennent assez souvent :

départ -> "."
départ -> instruction
départ -> branch

"." -> commande

instruction -> argument1

branch -> label

argument1 -> ","

etc...


Le but étant de définir ces règles et le comportement qui s'ensuit à partir de ces règles.

5

Ok, donc c'est bien ce que je suis en train de faire. Merci de m'avoir rassuré. smile

Ceci dit, si jamais certains ont des liens, même assez scolaires, pour ce type de programmation, je suis preneur. smile

6

[edit] cross avec naPO, mais puisque j'ai commencé à écrire un pavé autant le poster embarrassed




La grammaire d'un langage est ce qui va définir quelles constructions sont valides dans ton langage et quelles constructions ne le sont pas. Il s'agit tout simplement de règles qui définissent les différentes structures disponibles dans ton langage et qui les décomposent en sous-structures jusqu'à arriver à la description la plus unitaire possible. Expliqué en français, on pourrait imaginer une grammaire comme ça (pour un pseudo-langage style "C" par exemple) :
- Une condition s'écrit avec le mot "if" suivi d'une parenthèse ouvrante "(", puis d'une expression, puis d'une parenthèse fermante ")", et un bloc d'instructions
- Un bloc d'instructions s'écrit soit sous la forme d'une instruction, soit avec une accolade ouvrante "{", un nombre quelconque d'instructions, puis une accolade fermante "}" - Une instruction peut être composée soit d'une expression, soit ...


Bien sûr c'est très lourd à écrire en français, et il existe plus ou moins un formalisme pour décrire une grammaire. J'ai la flemme d'aller vérifier, mais de mémoire ça ressemble un peu à ça (de toutes façons tu n'en as pas tellement besoin a priori, c'est juste pour information) :

condition ::= "if" "(" expression ")" instructions_bloc

instructions_bloc ::= instruction
                    | "{" (instruction)* "}"

instruction ::= expression
              | ...

Cette grammaire pourrait être un point de départ pour un langage qui accepterait des constructions de type "if (blabla) { echo 'plop'; }", "blabla" étant une expression et "echo 'plop';" une instruction. Ce qui est intéressant avec ce concept de grammaire, c'est qu'il est finalement assez proche de la façon dont tu peux t'y prendre pour écrire un parseur.

Imaginons la fonction "parser_bloc_instructions" par exemple. Elle est en fait relativement simple, puisqu'on vient de dire qu'un bloc d'instructions pouvait être composé de deux choses : soit une instruction, soit une liste d'instructions entre accolades. Il suffit donc de tester le prochain mot sur lequel on tombe. Si c'est une accolade, alors tu es dans le deuxième cas. Si le programme est correct syntaxiquement, on va donc avoir une série d'instructions puis une accolade fermante pour finir. Tu vas donc pouvoir appeler ta fonction "parser_instruction" en boucle tant que le prochain mot qui vient n'est pas une accolade fermante. Quand tu trouveras cette accolade fermante, plutôt que d'appeler une fois de plus "parser_instruction", tu vas quitter ta fonction "parser_bloc_instructions" : tu as reconnu un bloc d'instruction complet.

Tout le reste peut fonctionner sur le même principe : il ne s'agit finalement que d'un ensemble de fonctions qui sont chacune prévues pour reconnaitre une construction en particulier. Si cette construction est en fait une combinaison de sous-constructions, alors tu vas juste avoir à appeler les fonctions qui gèrent toutes ces constructions une à une. Finalement les fonctions de "bout de chaine" ne sont pas si nombreuses, et au moins ça t'évite d'avoir à écrire 50 fois la fonction qui parse un nombre par exemple : tu l'écris une seule fois, et tu l'appelleras pour chaque construction qui fait intervenir un nombre à un moment où à un autre (comme par exemple les expressions arithmétiques).

Pour simplifier les choses, tu peux séparer en deux : un "lexeur" (je ne sais pas si il y a un équivalent en français ?) dont le seul but est de récupérer le prochain "mot" dans ton texte. Par mot j'entends une entité indivisible au sein de ta grammaire : un nombre, un opérateur, un mot clé, etc... Ce lexeur pourra être ensuite appelé depuis tes fonctions de parsing pour les alléger au maximum (pas la peine d'aller se taper de la lecture de chaine et de la comparaison de caractères en plein milieu d'une fonction supposée analyser la construction grammaticale d'un programme). Autre avantage : ce lexeur peut aussi véhiculer les informations du style "numéro de ligne" et "numéro de colonne" que tu afficheras en cas d'erreur, sans avoir à t'en soucier ailleurs.

(désolé si ces explications sont un peu fouilli et surtout pas très académiques grin)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

7

Et pour les implémentations existantes et autres explications : http://fr.wikipedia.org/wiki/Lex_et_yacc.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

8

Bon, puisque le sujet Lex/Flex et Yacc/Bison est incontournable, autant ajouter un petit mot dessus : il s'agit de programmes avec lesquels tu vas respectivement pouvoir générer un lexeur et un parseur en indiquant d'un coté les "mots" valides de ton langage (sous la forme d'expressions régulières par exemple) et d'un autre la grammaire avec un formalisme proche de celui du ./6.

Avis perso : ça permet d'obtenir très vite un lexeur/parseur et donc ça peut être utile pour des petits programmes que tu veux réaliser rapidement, mais ça génère un code absolument immonde (aussi bien point de vue lisibilité qu'optimisation). Pour un "vrai" projet, et à fortiori si tu le fais en partie pour le plaisir d'apprendre, mon seul conseil serait de ne surtout jamais toucher à ces outils ^^
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

9

Mille fois merci !

C'est très clair, mais je ne sais pas, vu la simplicité de l'assembleur 68000, si j'ai intérêt à décrire toute une grammaire formellement pour ensuite faire marcher une moulinette. Surtout que mes instructions 68k à proprement parler sont enregistrées sous cette forme-là :
|movem
	INSTRUCTION	0x4880,TWO_OP+OTH_OP+OP_REG_MASK,SIZE_WL+DL_MASK3,AM_WRT+AM_AN_IND+AM_AN_DEC+AM_X_AN+AM_X_AN_XN+AM_XW+AM_XL	|+ AM_SRC ?
	INSTRUCTION	0x4c80,TWO_OP+OP_REG_MASK2,SIZE_WL+DL_MASK3,AM_WRT+AM_AN_IND+AM_AN_INC+AM_X_AN+AM_X_AN_XN+AM_XW+AM_XL+AM_PCREL

Donc quand je tombe sur une instruction, en plus du masque, je sais ce qu'elle a le droit de trouver après ou pas, je compte me servir de cette table. j'aurai juste à savoir lire les 14 adressages du 68k (ça me semble pas encore énorme). Après, pour les commandes xdef/include/incbin/etc..., c'est vraiment du cas par cas.
Zephyr (./6) :
Ce lexeur pourra être ensuite appelé depuis tes fonctions de parsing pour les alléger au maximum (pas la peine d'aller se taper de la lecture de chaine et de la comparaison de caractères en plein milieu d'une fonction supposée analyser la construction grammaticale d'un programme

Je me suis trouvé avec une merde : impossible d'utiliser les fonctions de string.h ! En effet, dans le source, rien ne se termine par #0, c'est EOF... On peut donc avoir "|" (commentaires), ";" (équivalent à \n, mais on reste sur la même ligne), un espace, une tabulation, une virgule dans certains cas (xdef), EOL, etc... Donc une solution consiste à vérifier la présence de tous ces éléments pour détecter la fin de la chaine, un peu partout dans le code, à chaque fois qu'on doit vérifier une chaine (gros bordel bien chiant à écrire, sûrement très très drôle à débugguer). L'autre solution consiste à recopier dans un buffer (de quelle taille ?) et de faire terminer par un #0, puis d'utiliser des fonctions classiques de comparaisons ou de recherche. Bref, c'est la joie !!! grin


Je me pose toutes ces question, parce qu'hier, j'ai codé le comportement des labels (vérification de la validité, création d'un checksum et ajout dans la table kivabien) et des xdef (checksum et ajout dans la table.
Donc dans les deux cas, je m'occupe d'un label. Ca a l'air d'être la même chose, un label et un label. Mais non, rien à voir ! Dans un cas, je m'arrête aux ":", et si je trouve un caractère non alphanumérique (ou ".", valide dans un label), l'assembleur gueule et s'arrête. C'est très simple.
Dans l'autre, faut savoir où s'arrête le label. On a le choix entre EOL, EOF, ";", "|", ",", <espace>, <tab>. C'est déjà plus lourd à vérifier ! Qui plus est, si on a des espaces, puis des tabs, et enfin une virgule, on repart sur un nouveau label ! Jouissif. J'ai pas fini de me prendre le choux pour trouver une description générique et efficace d'un source valide. grin

10

Zephyr (./8) :
Pour un "vrai" projet, et à fortiori si tu le fais en partie pour le plaisir d'apprendre, mon seul conseil serait de ne surtout jamais toucher à ces outils ^^

#crayzza# T'as tout compris grin

11

En L3, on avait implémenté un assembleur (enfin, je veux dire, "un programme pour transformer en binaire du code source écrit en langage d'assemblage" grin) ARM. Pas de linker, on se limitait à un fichier unique par exécution, qui devait contenir tous les labels utilisés.
en utilisant pour la reconnaissance de syntaxe:
* le lexer fourni par les profs (qu'on a, si je me souviens bien, dû modifier un peu...). C'est le code qui découpe en morceaux de même nature (entier, identificateur, virgule, registre, commentaire, fin de ligne, etc.) les octets du flux d'entrée. Il skippe tout ce qui est blanc non significatif (tabs, espaces, etc.). Ces morceaux de même nature sont des LEXical EleMents (lexem, "lexème" en français).
* l'analyse syntaxique (que nous avons dû écrire à la main) qui se base sur la grammaire et utilise le lexer. Il a une organisation hiérarchique du genre (de mémoire, modifié un poil pour une syntaxe M68k)
... ReadProgram(...) {
    EndOfFile = 0
    while (EndOfFile == 0) {
        ReadLine(...)
    }
}

... ReadLine(...) {
    Lexem l = ReadLexem(...) // appel du Lexer.
    if (LexemType(l) == Identifier) {
        // Le premier lexem de la ligne est un identificateur. Cette ligne risque de contenir un label, c'est à dire un ":" après. Ou alors c'est une instruction (par exemple).
        if (DecodeInstruction(l) == FALSE) {
            // Non, ce n'était pas une instruction
            Lexem l2 = ReadLexem(...)
            if (LexemType(l2) == Colon) { // Deux points
                // On vient de lire un label. Le stocker ça dans la table des symboles.

                // Ce qu'on fait maintenant *dans ce cas* dépend de la grammaire.
                // Si la grammaire dit qu'une ligne ne doit contenir qu'un label xor une instruction xor ...
                // (modulo l'introduction de commentaires un peu partout, si on les gère autrement que du whitespace),
                // et que maintenant qu'on a lu un label, ReadLexem(...) ne rend pas un lexem de type EOL,
                // on se plaint à l'utilisateur qu'il nous rentre des choses qu'on ne comprend pas.
            }
        }
        else {
            // Tiens, on vient de lire un mnémonique d'instruction (ex. "move"). Alors le prochain truc qu'on doit lire doit être un "."
            Lexem l2 = ReadLexem(...)
            if (LexemType(l2) == Dot) {
                // C'est bon, maintenant on doit lire un identificateur "b"/"w"/"l"
                Lexem l3 = ReadLexem(...)
                if (IsSizeQualifier(l3)) { 
                    // Maintenant on doit lire un registre, ou alors un identificateur

                    // Au coup suivant, si c'est une instruction qui a plusieurs opérandes, on doit lire une virgule. Et caetera (faut que j'aille bouffer :D).
            }
            else {
                 // On se plaint à l'utilisateur qu'il n'a pas mis de . après la mnémonique d'instruction.
            }
        }
}


* une analyse sémantique, par-dessus l'analyse syntaxique. Ca vérifiait par exemple, après avoir tout lu, si on ne faisait pas référence à des labels pas définis dans ce fichier (mais ça peut être étendu pour gérer des références externes, si tu introduis une directive assembleur avec cette sémantique).


Avantage des générateurs de parsers à partir d'une grammaire: facilité d'écriture pour la personne qui les utilise (il suffit d'avoir la grammaire du langage).
Défaut: on peut faire un peu plus efficace si on écrit le parser à la main.


[EDIT: quelques retours à la ligne supplémentaires permettent de ne pas casser la mise en page...]
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

12

t1 mais y'a que moi qui ai fait du parsing de C avec du Caml sick

13

Ok, merci Lionel, c'est en fait ce que je fait (avec la fonction du même nom que chez toi ^^) :
	|==================================================================
	|	Check for EOL or EOF
	|==================================================================
ReadLine:
	cmpi.b	#0x0d,(%a3)				|EOL ?
	bne.s	NoNewline				|no
		addq.l	#1,%a3				|else get next char
		addq.w	#1,%d4				|update line counter
		bra.s	ReadLine			|and loop
NoNewline:
	tst.b	(%a3)					|EOF ?
	beq	EndOfFile				|yes, quit now
	
	|==================================================================
	|	Skip spaces and tabulations
	|==================================================================
	cmpi.b	#0x20,(%a3)				|space ?
	bne.s	NoSpace					|no
		addq.l	#1,%a3				|skip it
		bra.s	ReadLine			|restart line parsing
NoSpace:
	cmpi.b	#0x09,(%a3)				|tab ?
	bne.s	NoTab					|no
		addq.l	#1,%a3				|skip it
		bra.s	ReadLine			|restart line parsing
NoTab:
	|==================================================================
	|	Look for a comment (pipe)
	|==================================================================
	cmpi.b	#'|',(%a3)				|comment ?
	bne.s	NoComment				|yes, comment can begin with |
SkipLineComment:
		cmpi.b	#0x0d,(%a3)+			|EOL ?
		bne.s	NoEOL				|no
			addq.w	#1,%d4			|else update line counter
			bra.s	ReadLine		|and parse next line
NoEOL:
		tst.b	(%a3)				|EOF ?
		beq	EndOfFile			|yes, quit file parsing
		bra.s	SkipLineComment			|else check next char
NoComment:

[...]

Hors de question d'utiliser nu de vos trucs automatique, ça me tente pas, ça me fait plutôt fuir qu'autre chose. grin

YN -> t1 autant j'aime les langages fonctionnels (lisp/scheme), autant je me verrais pas faire un parseur avec ça trinon

14

Oooh. J'ai explosé la mise en page avec mon bloc pre de ./11. Je vais éditer ça...

G++/GCC est passé d'un parser auto-généré à un parser fait à la main, pour plus d'efficacité.

ReadLine: 
	cmpi.b	#0x0d,(%a3)				|EOL ? 
	bne.s	NoNewline				|no 
		addq.l	#1,%a3				|else get next char 
		addq.w	#1,%d4				|update line counter 
		bra.s	ReadLine			|and loop 
NoNewline:

Si ReadLine est (et c'est probable) un label de début de fonction, non

NewLine:
	addq.l	#1,%a3				| get next char 
	addq.w	#1,%d4				|update line counter 

ReadLine: 
	cmpi.b	#0x0d,(%a3)				|EOL ? 
	beq.s	Newline				|no 
NoNewline:

magic

GCC peut faire des optimisations de ce genre. Mais l'optimisation prématurée, c'est assez mal ^^


J'ai entendu dire à plusieurs reprises, par des gens qui aiment bien ces langages, que les capacités de "pattern matching" et de représentation arborescente "transparente" de certains langages type ML les rendent bien adaptés à du parsing. Mais je ne connais pas assez ces langages pour avoir une idée là-dessus grin
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

15

Folco (./13) :
YN -> t1 autant j'aime les langages fonctionnels (lisp/scheme), autant je me verrais pas faire un parseur avec ça  trinon.gif
Au contraire les parsers ça se fait très bien en fonctionnel, la transformation texte -> tokens c'est une opération purement fonctionnelle.
avatar
Combien de tas de bois une marmotte pourrait couper si une marmotte pouvait couper du bois ?

16

Twindruff (./15) :
Au contraire les parsers ça se fait très bien en fonctionnel, la transformation texte -> tokens c'est une opération purement fonctionnelle.

Ah bien, j'ai pas dû assez y réfléchir alors cheeky


Lionel -> pas mal en effet, mais vu ma question, tu dois te rendre compte que je suis encore dans le brouillard, donc ce genre d'optimisations de deux octets, pour le moment, c'est du domaien de la pelle à tarte grin Ca pourra venir, mais comme tu dis, si j'aime écrire proprement quand je code, je suis pas un fan de l'optimisation intégrale dès le premier jet, pas avant que ce soit très clair dans ma tête tout au moins cheeky (j'écris aussi des bsr/rts au début, je me garde ce genre de conneries pour la fin, une fois que tout marche bien cheeky)

17

!call Pollux
--- Call : Pollux appelé(e) sur ce topic ...


J'aimerais son avis, il est souvent de très bon conseil le monsieur cheeky

18

Nan, mais je suis d'accord: l'optimisation intégrale dès le premier jet, c'est TRES mal grin
Dernièrement, j'ai customisé une routine d'ExtGraph pour lachprog: j'ai écrit la modification de façon naïve. Peut-être que si je m'y remettais, je pourrais gagner deux ou trois instructions par le fait que
(not a) and (not b) == not (a or b)
ou
(not a) or (not b) == not (a and b)
(je ne sais plus laquelle des deux s'applique dans cette routine)


Pollux est en effet de très bon conseil, mais on ne l'a pas trop vu, ces derniers temps. Ou du moins, il n'a pas beaucoup posté.
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

19

Comme les autres, je te conseille d’écrire un analyseur lexical, puis un analyseur syntaxique, ça t’évitera d’avoir les mêmes bouts de code déchiffrant le texte à plusieurs endroits dans ton code.
Pour l’analyseur lexical, je suggère un aiguillage en fonction du premier caractère du lexème que tu rencontres (genre est-ce une lettre, un opérateur, etc.), puis un code assez naïf pour réaliser l’identification complète du lexème (est-ce une étiquette, une instruction, une opérande, etc.).
Pour l’analyseur syntaxique, je te conseille d’écrire ta grammaire de façon à ce qu’elle ne présente aucun conflit LL(1), puis, à partir de ça, de pondre mécaniquement ton code comme l’a proposé Zephyr.
Je pense que pour un projet d’assembleur 68k, ce n’est pas nécessaire de t’encombrer avec des méthodes d’analyse plus sophistiquées mais aussi plus complexes à maîtriser.
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

20

Yoshi Noir (./12) :
t1 mais y'a que moi qui ai fait du parsing de C avec du Caml sick.gif


non ( et CAML c'est super adapté pour ce genre de trucs smile )
«Les gens exigent la liberté d’expression pour compenser la liberté de pensée qu’ils préfèrent éviter.» - Sören Kierkegaard

La République, c’est comme la syphilis : quand on l’a attrapée, soit on se fait sauter le caisson, soit on essaie de vivre avec.

21

Merci Sasume, mais ta manière de dire les choses me brouille, vu que tu dis que tu vois les choses comme les autres. grin

Quand tu dis :
ça t’évitera d’avoir les mêmes bouts de code déchiffrant le texte à plusieurs endroits dans ton code.

Ca me semble aller à l'encontre de ce que dis nEUrOO en ./2 . Qui plus est, ça introduirait pas mal de sauts pour des fonctions qui de toute façon, ne prennent pas une place folle (genre une fonction SkipSpaces pour les tabs/spaces, petite perte de place en réimplémentant à chaque fois, mais petit gain de vitesse aussi). En fait, ma grande crainte est, au final, de mettre 10 minutes pour assembler 50 octets. grin

Quant à ta manière de dire "est-ce une étiquette, une instruction, une opérande, etc.", ça me semble aller également à l'encontre de ce qu'on m'a dit. J'ai écrit de la sorte (en prenant pour exemple un xdef) :
début de ligne :
- je skippe les espaces
- je trouve un '.'
- je fais une recherche de la commande dans la table des chaines des commandes
- j'ai trouvé la commande, je vais exécuter son code (via une table de saut pc-relative dans mon cas)
- puisque c'est un xdef, il me faut un label.
- je vérifie que le premier caractère est une lettre ou un underscore
- je vérifie ensuite que j'ai un {nombre/lettre/point/underscore} et je loop dans la lecture, ou un {espace/tabulation/EOL/EOF/commentaire/point-virgule}.
- une fois trouvé la fin, s'il n'y a pas d'erreur évidemment, j'ajoute mon label dans la table du prévue à cet effet dans le fichier objet

En fait, cette méthode me semble le contraire de ce que tu me proposes, non ?

22

Folco (./9) :
Je me suis trouvé avec une merde : impossible d'utiliser les fonctions de string.h ! En effet, dans le source, rien ne se termine par #0, c'est EOF... On peut donc avoir "|" (commentaires), ";" (équivalent à \n, mais on reste sur la même ligne), un espace, une tabulation, une virgule dans certains cas (xdef), EOL, etc... Donc une solution consiste à vérifier la présence de tous ces éléments pour détecter la fin de la chaine, un peu partout dans le code, à chaque fois qu'on doit vérifier une chaine (gros bordel bien chiant à écrire, sûrement très très drôle à débugguer). L'autre solution consiste à recopier dans un buffer (de quelle taille ?) et de faire terminer par un #0, puis d'utiliser des fonctions classiques de comparaisons ou de recherche. Bref, c'est la joie !!! grin

http://tigcc.ticalc.org/doc/string.html#strncmp
Lionel Debroux (./14) :
ReadLine: 
	cmpi.b	#0x0d,(%a3)				|EOL ? 
	bne.s	NoNewline				|no 
		addq.l	#1,%a3				|else get next char 
		addq.w	#1,%d4				|update line counter 
		bra.s	ReadLine			|and loop 
NoNewline:

Si ReadLine est (et c'est probable) un label de début de fonction, non

NewLine:
	addq.l	#1,%a3				| get next char 
	addq.w	#1,%d4				|update line counter 

ReadLine: 
	cmpi.b	#0x0d,(%a3)				|EOL ? 
	beq.s	Newline				|no 
NoNewline:

magic

sick
[ul][li]C'est moche de mettre du code d'une fonction en dehors de la fonction.[/li][li]La fonction (dans les 2 versions) n'est pas conforme à la convention d'appel (pas le droit de modifier %a3 et %d4 dans une fonction).[/li][/ul]
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

23

De toute façon, oui, il y aura certaines parties presque redondantes, mais le fait d’écrire un analyseur lexical permet à ton analyseur syntaxique de travailler directement sur les lexèmes et donc d’éviter ceci :
Folco (./1) :
Ca m'oblige, pour chaque fonction rencontrée, à réécrire la détection des lettres etc..., même si j'ai quelques fonctions (CheckAlpha, CheckNum etc...).


D’après la suite de ton ./21 tu réalises l’analyse lexicale et syntaxique en même temps, du coup, effectivement, tu te retrouves avec un code hyper répétitif.

Le fait d’identifier complètement les lexèmes que tu rencontres avant de t’intéresser à la syntaxe du code source analysé simplifie largement l’écriture de l’analyseur syntaxique, celui-ci gère un flux de lexèmes correspondant aux parties pertinentes du code source à assembler (les commentaires et les espaces sont zappés, les macros sont déjà substituées, etc.) et il n’a plus qu’à s’occuper de vérifier que ces lexèmes mis bout à bout forment des expressions valides et à évaluer leur sens (enfin, en réalité ça c’est du ressort de l’analyseur sémantique, mais dans ton cas on peut regrouper l’analyse syntaxique et sémantique sans que ça ne rende le code trop bordélique).
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

24

Mais j'utilise a3 et d4 comme des registres globaux, ils sont passés à toutes les fonctions avec ce qu'ils signifient, les fonctions peuvent les modifier mais c'est spécifié, et elles ne peuvent détruire que les habituels d0-d2/a0-a1. M'enfin, on est dans le parsing de source, là. smile

Et sinon, merci pour strncmp, ceci dit, ça ne m'empêchera pas de vérifier qu'après un symbole, il n'y a pas d'autres caractères (donc je devrais trouver toujours ';', ',', '|', EOL, EOF, 0x20, 0x09, ou alors vérifier qu'il n'y a pas de chiffre/lettre/point/underscore. Et ça oblige à calculer la longueur de la chaine qui me sert de référence pour la comparaison. J'ai implémenté une comparaison "on-the-fly", ça me semble être le plus rapide.

25

Merci Sasume. Je pense que je vais tout de même procéder de manière assez naïve, j'ai peur d'écrire une usine à gaz sinon... C'est peut-être de l'incompétence, n'y vois pas autre chose...

26

Voici une autre façon d’exprimer mon idée :

Dans ton cas, les fichiers source à assembler sont des suites d’expression correspondant soit à des directives d’assemblage (xdef label), soit à des instructions (move.w d0,d1), soit à des données (dc.w $C8, mais on peut considérer que ces expressions sont équivalentes à des instructions en fait). Les instructions sont formées d’un opcode (move, add, etc.), éventuellement de l’opérateur « . » suivi de la taille des opérandes, et des opérandes. Ces derniers peuvent être des noms de registre (a0, d7, etc.), des adresses absolues, des données, etc.
Bref, il faut procéder ainsi pour décrire formellement la totalité du langage que ton assembleur sera capable de lire.
Dans cette description, les opcodes, opérateurs, noms de registres, etc. sont les différentes classes de lexèmes que tu devras identifier. Par exemple, dans la langue française on utilise des noms, des adjectifs, des verbes, etc. et bien là on a affaire à des opcodes, opérateurs, etc. Le rôle de ton assembleur sera dans un premier temps d’identifier si ces lexèmes mis bout à bout correspondent à une « phrase » correcte. Pour ce faire, il est plus simple de manipuler directement ces lexèmes plutôt que le code source, d’où l’intérêt d’un analyseur lexical qui transforme le fichier source (texte) en une suite de lexèmes qui seront mis à disposition de l’analyseur syntaxique.
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

27

Mais pour de l'asm sur une plateforme limité, autant faire à l'arrache et fusionner, non?
«Les gens exigent la liberté d’expression pour compenser la liberté de pensée qu’ils préfèrent éviter.» - Sören Kierkegaard

La République, c’est comme la syphilis : quand on l’a attrapée, soit on se fait sauter le caisson, soit on essaie de vivre avec.

28

Ok. Tu vois donc ça en deux passes (création d'un fichier intermédiaire avant même le fichier objet), ou une seule (encodage d'une ligne en lexèmes puis analyse syntaxique) ?

(cross)

29

./27 Je pense que ça ne ferait qu’augmenter la taille de l’exécutable et rendre le code plus difficile à maintenir pour un gain de performance moindre.

./28 Pas besoin de créer un fichier intermédiaire, tu peux te débrouiller en lisant les lexèmes au fur et à mesure.

AnaSyntax_Expression:
  Lexème lex = AnaLex_LexèmeSuivant()
  Si lex est de classe « Directive »
    AnaSyntax_Directive()
  Sinon, si lex est de classe « Opcode »
    AnaSyntax_Instruction()
  Sinon, si lex est de classe « Identificateur »
    AnaSyntax_Étiquette()
  Sinon
    AnaSyntax_SyntaxError()

AnaLex_LexèmeSuivant:
  Lex lex = lexème vide
  Char c = caractère suivant du fichier source
  Tant que c est un espace ou une tabulation
    c = caractère suivant
  Si c est un caractère alphabétique
    lex.classe = « Identificateur »
    lex.valeur = ReconnaitreIdentificateur()
    Si lex.valeur correspond à une directive d’assemblage
      lex.classe = « Directive »
    Sinon, si lex.valeur correspond à une opcode
      lex.classe = « Opcode »
  Sinon, si c est « . »
    lex.valeur = Opérateur_Point
    lex.classe = Opérateur
  Retourne lex


C’est juste pour que tu voies comment on peut s’y prendre, mais il y a d’autres façons de faire.
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

30

ça revient exactement à faire à l'arrache si tu le fais lors de la même passe
«Les gens exigent la liberté d’expression pour compenser la liberté de pensée qu’ils préfèrent éviter.» - Sören Kierkegaard

La République, c’est comme la syphilis : quand on l’a attrapée, soit on se fait sauter le caisson, soit on essaie de vivre avec.