Cette question consiste à construire des arbres d'expression personnalisés dans .NET à l'aide des opérateurs trouvés dans C # (ou dans une autre langue). Je fournis la question avec certaines informations de base.
pour mon géré 2 phases 64 bits Assembleur J'ai besoin de soutien aux expressions. Par exemple, on peut vouloir assembler: p> l'expression Cette expression n'est pas évaluée lorsqu'elle est construite. Au lieu de cela, il est évalué plus tard dans le contexte de mon assembleur (lorsqu'il détermine les décalages de symboles et tels). Le .NET Frameword (puisque .NET 3.5) fournit un soutien aux arbres d'expressions, et il me semble qu'il est idéal pour ce type d'expressions qui sont évaluées plus tard ou ailleurs. P> mais je ne le fais pas Savoir comment m'assurer que je peux utiliser la syntaxe c # em> (en utilisant +, <<,%, etc.) pour la construction de l'arborescence d'expression. Je veux empêcher des choses comme: p> Comment allez-vous ici? P> Remarque: j'ai besoin d'un arbre d'expression pour pouvoir Convertissez l'expression en une représentation de chaîne personnalisée acceptable et, en même temps, être capable de l'évaluer à un moment d'un moment autre que sa définition. P> Une explication de mon exemple: Cependant, cette question n'est pas vraiment sur la manière d'évaluer les expressions de l'assembleur, mais plus sur Comment évaluer toute expression qui a des classes personnalisées en eux em> (pour des choses comme les symboles et 64 - $ + mystring code> ne doit pas être une chaîne mais une expression valide réelle avec le Avantages de la syntaxe et de la vérification de type et de l'intellensense dans VS, quelque chose du long des lignes de: p>
64 - $ + mystring code>. Le code> code> est le décalage actuel. Il s'agit donc d'un numéro spécifique inconnu à l'avance (mais connu au moment de l'évaluation). Le
mystring code> est un symbole qui peut être connu ou non à l'heure d'évaluation (par exemple, quand il n'a pas encore été défini). Soustraire une constante C à partir d'un symbole S est la même que
s + -c code>. Soustraire deux symboles S0 et S1 (
S1 - S0 code>) donne la différence entier entre les valeurs de deux symboles. P>
$ code> dans l'exemple) et comment vous assurer qu'il peut toujours être jolie- imprimé à l'aide d'un visiteur (gardant ainsi l'arbre). Et puisque le .NET Framework a ses arbres d'expression et ses visiteurs, il serait agréable d'utiliser celles-ci, si possible. P> p>
4 Réponses :
C # prend en charge l'attribution d'une expression Lambda à une expression Vous pouvez alors potentiellement prendre l'arborescence d'expression générée et la convertir en arbre de syntaxe de votre assembleur, mais cela ne semble pas être tout ce que vous recherchez, et je n'ai pas Ne pense que vous allez être capable de tirer parti du compilateur C # pour le faire pour une entrée arbitraire. P> Vous allez probablement finir de construire votre propre analyseur pour votre langage de montage, comme je Ne pensez pas que le compilateur C # va faire ce que vous voulez dans ce cas. p> p>
J'ai examiné les expressions de Lambda et le problème réel que je devienne apparent lorsque j'essaye ceci: expression
Je ne sais pas ce que vous cherchez exactement, mais ce qui suit est une approche esqueuse que je pense fonctionnerait.
NOTE I P>
$ code> (offset actuel). Ce serait probablement sûr d'un registre (?) Li>
- ne montre pas explicitement l'opérateur
unaire / binaire - code> au travail. Cependant, la mécanique est en grande partie la même. Je me suis arrêté de l'ajouter parce que je ne pouvais pas travailler la sémantique des expressions d'échantillons de votre question (je pense em> qui soustraire l'adresse d'une chaîne connue n'est pas utile, par exemple) sub> li>
- L'approche ne place pas (Sémantican) Limites: vous pouvez compenser toute iereférence dérivée de la viande de référence. En pratique, vous ne voudrez peut-être pas qu'aucun niveau d'indexation et définir l'opérateur
+ code> directement sur symboliseReference serait plus approprié. LI>
-
a sacrifié style de codage à des fins de démonstration (en général, vous ne voulez pas répéter compiler () code> vos arbres d'expression et évaluation directe avec . Composition () ( ) code> a l'air laid et déroutant. Il est laissé au PO pour l'intégrer de manière plus lisible em> p> li>
-
La démonstration de l'opérateur de conversion explicite est vraiment hors sujet. J'ai été emporté de glandlty (?) P> Li>
- Vous pouvez observer le code exécution en direct sur ideone.com strong> li>
ol>
. p> xxx pré> p>
Légèrement mis à jour; J'attendrai un peu de retour avant de se développer dans des directions inutiles
Merci d'avoir pris le temps d'écrire un message aussi étendu. Je pense que votre solution est dans la bonne direction, mais certains points clés ne me sont pas clairs ou manquants: 1) je veux évaluer les symboles (votre evaladdress () code>) plus tard, lorsque j'évalue toute la fonction . Cela ne devrait pas être difficile, juste en passant l'état
code> dans la fonction. 2) Je veux garder l'arbre afin que je puisse le marcher avec un visiteur d'une arbre d'expression et imprimer ma propre représentation de chaîne de chacune des classes personnalisées. Essayez ceci par exemple:
10 + Ref1 + 20 + Ref2 code>, comment cela fonctionnerait-il?
@VirtLink: Réponses rapides 1) En effet, vous aurez toujours un état de programme lors de la phase d'émission de fonctionnement; J'ai choisi de le transmettre à (Kiss) mais vous pouvez utiliser "singleton" et / ou ioc. 2) Le code fait déjà cela (seulement la compilation des arbres afin de les évaluer). Conservez simplement avec l'expression <> code> les arbres aussi longtemps que possible. Sur l'impression: mon code fait déjà cela (voir Tostring). Si vous vouliez des transformations plus avancées / flexibles (optimisation, Annotation NEstNot), vous devriez choisir un motif de visiteur (utilisez Visitor + Builder pour effectuer une AST sur des transformations AST, par exemple)
Encore une fois, pas tout à fait sûr si c'est exactement ce que vous recherchez, mais à partir du point de départ de vouloir créer une sorte d'arborescence d'expression à l'aide de la syntaxe C #, j'ai proposé ...
Pretty printed: 100 * Ref(Test) + 50 Evaluated: 3050
Merci d'avoir pris le temps de répondre à ma question. Votre direction est la même direction que je suis allée quand j'ai rencontré ce problème. Vous ne semblez utiliser aucune des choses de l'arbre d'expression disponible depuis 3.5. Peut-être que ce n'est tout simplement pas possible de faire ce que je veux avec des arbres d'expression. Néanmoins ... Comment allez-vous évaluer cela?
Comme indiqué dans ma réponse précédente, tandis que les arbres d'expression et la capacité de compiler à eux sont des fonctionnalités très utiles, vous devez connaître la signature de fonction exacte pour créer l'expression appropriée
Vous mettez en place une assembleuse à deux phases (Pass?)? Le but d'un assembleur de deux passes
est de gérer références avant em> (par exemple, un symbole indéfini lors de la première rencontre). P>
Alors, vous n'avez pas besoin de construire un arbre d'expression. P>
en phase (passe 1), vous analysez le texte source (par n'importe quel moyen que vous aime comme: parseur ad hoc, descente récursive, générateur d'analyseurs) et collecter des valeurs de symboles (en particulier les valeurs relatives des étiquettes en ce qui concerne la Code ou section de données dans laquelle ils sont contenus. Si vous rencontrez une expression, vous essayez de l'évaluer à l'aide de l'évaluation de l'expression à la volée, impliquant généralement une pile de poussée pour les subexpressions et produisant un résultat final. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. Si vous rencontrez un symbole. dont la valeur est indéfinie, vous propagez la non-définition comme résultat d'expression. Si l'opérateur / commande d'assemblage a besoin em> la valeur d'expression pour définir un symbole (par exemple, x EQ A + 2) ou pour déterminer les compensations dans une section de code / de données (par exemple, DS x + 23), puis la valeur doit être définie ou l'assembleur jette une erreur. Cela permet à Org A + BC de fonctionner. Autres opérateurs d'assemblage qui n'ont pas besoin de la valeur pendant la passer une seule ignorer le résultat indéfini (par exemple, la charge ABC ne se soucie pas de ce que ABC est, mais peut de Terminer la longueur de l'instruction de charge). P>
en phase (PASS II), vous préparez le code de la même manière. Cette fois, tous les symboles ont des valeurs, toutes les expressions devraient donc être évaluées. Ceux qui devaient avoir une valeur dans la phase I sont contrôlés contre les valeurs produites dans la phase II pour s'assurer qu'elles sont identiques (sinon, vous obtenez une erreur de phase). D'autres opérateurs d'assemblage / instructions ont maintenant suffisamment d'informations pour générer les instructions de la machine ou les initialisations de données. P>
Le point est que vous ne devez jamais construire un arbre d'expression. Vous évaluez simplement l'expression lorsque vous le rencontrez. P>
Si vous avez construit un em> un assembleur de passe em>, vous devrez peut-être modéliser l'expression pour permettre la réévaluation plus tard. J'ai trouvé plus facile de produire un vernis inverse comme séquence de "push valeur em>" et arithop em> et stocker la séquence (équivalente à l'arborescence d'expression), car il est dense (arbres ne sont pas) et trivial pour évaluer en faisant une analyse linéaire en utilisant (comme ci-dessus) une petite pile de poussée. p>
En fait, ce que j'ai fait était de produire un vernis inverse qui agit en fait comme la pile d'expression elle-même; Au cours d'une analyse linéaire, si les opérandes pouvaient être évaluées, ils ont été remplacés par une commande "Push valeur em>" et que le vernis inversé restant est pressé pour éliminer la bulle. Ce n'est pas cher parce que la plupart des expressions sont en réalité minuscules. Et cela signifiait que toute expression qui devait être sauvée pour une évaluation ultérieure était aussi petite que possible. Si vous enfilez les commandes Push Identifier em> via la table des symboles, lorsque le symbole est défini, vous pouvez remplir toutes les expressions partiellement évaluées et les réévaluer; Ceux qui produisent une seule valeur sont ensuite traités et leur espace recyclé. Cela m'a permis d'assembler des programmes géants dans un mot de 4k, 16 bits, de retour en 1974, car la plupart des références en avant n'atteignent pas vraiment très loin. P>
Vous êtes correct, mais mon assembleur n'utilise rien. Toutes les instructions, expressions, sections, symboles et fichiers d'objets sont des objets, de sorte qu'ils puissent être créés à partir d'autres applications C # directement. Les expressions sont également créées par une telle application C #, puis évaluée par mon assembleur. J'ai besoin de l'arborescence d'expression pour pouvoir joliment-impression du modèle d'objet dans par exemple le langage Nasm (pour le débogage).
Donc, ce que vous voulez vraiment, c'est un arbre de syntaxe abstrait pour votre assembleur (résultat de l'analyse classique). Vous pouvez insister sur vos expressions directement c # évaluables, ce qui rend plus difficile leur produire comme vous l'avez signalé. Mais pour tout ce qui n'est pas une "expression" (par exemple, une spécification d'instructions de charge), vous devez toujours écrire quelque chose pour grimper sur cet arbre et produire la valeur finale. Les expressions étant vraiment i> faciles à évaluer ne sont qu'une partie de cela. Je ne suis pas sûr de voir les expressions évaluables de la valeur réelle C #. Et vous avez toujours le problème de deux passes à résoudre.
@Irabaxter: +1 pour une belle écriture; Bon style d'écriture aussi. Je suis prêt à parier que vous êtes un bon joueur d'échecs. Ne me demandez pas pourquoi :) Bravo!