Comment créer mon propre langage de programmation et un compilateur pour cela [fermé]


427

Je suis rigoureux dans la programmation et ai rencontré des langages tels que BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C ++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, Assembly, etc. Je ne comprends pas comment les gens créent des langages de programmation et conçoivent des compilateurs. Je ne comprenais pas non plus comment les gens créent des systèmes d'exploitation tels que Windows, Mac, UNIX, DOS, etc. L'autre chose qui est mystérieuse pour moi est la façon dont les gens créent des bibliothèques telles que OpenGL, OpenCL, OpenCV, Cocoa, MFC, etc. La dernière chose que je n'arrive pas à comprendre, c'est comment les scientifiques conçoivent un langage d'assemblage et un assembleur pour un microprocesseur. J'aimerais vraiment apprendre tous ces trucs et j'ai 15 ans. J'ai toujours voulu être un informaticien, comme Babbage, Turing, Shannon ou Dennis Ritchie.


J'ai déjà lu les concepts de Aho Compiler Design et Tanenbaum, et ils ne discutent que des concepts et du code à un niveau élevé. Ils n'entrent pas dans les détails et les nuances et dans la manière de concevoir un compilateur ou un système d'exploitation. Je veux une compréhension concrète pour pouvoir en créer une moi-même et pas seulement une compréhension de ce qu'est un fil, un sémaphore, un processus ou une analyse. J'ai demandé à mon frère à propos de tout cela. Il est étudiant SB à EECS au MIT et n'a pas la moindre idée de la création de toutes ces choses dans le monde réel. Tout ce qu’il sait, c’est une compréhension des concepts de conception de compilateur et de système d’exploitation, comme ceux que vous avez mentionnés (par exemple, tels que thread, synchronisation, concurrence, gestion de la mémoire, analyse lexicale, génération de code intermédiaire, etc.).


Si vous êtes sous Unix / Linux, vous pouvez obtenir des informations sur des outils dédiés: lex, yaccet bison.
mouviciel

Ma première suggestion serait de lire le livre du dragon par Aho. amazon.fr/Compilateurs-Principes-Techniques-Alfred-Aho/dp/…
Julian

1
Peut-être pas très utile, mais je vous recommande de consulter sites.google.com/site/steveyegge2/blog-rants (le blog de Steve Yegge) et steve-yegge.blogspot.com/ (l'autre blog de Steve Yegge).
KK.

3
Apprenez autant de langages de programmation que possible. De cette façon, vous apprendrez de leurs concepts et de leurs erreurs. Pourquoi se contenter de nains, quand on peut se tenir sur l'épaule de géants?
sbi

1
conseil: un interprète est plus facile qu'un compilateur; c'est juste une classe qui "fait quelque chose" en fonction du texte d'entrée lu ligne par ligne. autre astuce: associez-le à la réflexion pour pouvoir contrôler des objets arbitraires avec votre script.
Dave Cousineau

Réponses:


407

En gros, votre question est la suivante: "Comment les puces informatiques, les jeux d'instructions, les systèmes d'exploitation, les langages, les bibliothèques et les applications sont-ils conçus et implémentés?" C'est une industrie mondiale de plusieurs milliards de dollars qui emploie des millions de personnes, dont beaucoup sont des spécialistes. Vous voudrez peut-être concentrer votre question un peu plus.

Cela dit, je peux tenter ma chance à:

Je ne comprends pas comment les gens créent des langages de programmation et conçoivent des compilateurs.

Cela me surprend, mais beaucoup de gens considèrent les langages de programmation comme étant magiques. Quand je rencontre des gens lors de soirées ou quoi que ce soit, s'ils me demandent ce que je fais, je leur dis que je conçois des langages de programmation et que je mets en œuvre les compilateurs et les outils, et il est étonnant de constater le nombre de fois que des personnes - des programmeurs professionnels, par exemple - "wow, je n'y ai jamais pensé, mais oui, quelqu'un doit concevoir ces choses". C'est comme s'ils pensaient que les langues naissent tout à fait déjà complètement constituées d'infrastructures d'outils.

Ils n'apparaissent pas simplement. Les langues sont conçues comme n'importe quel autre produit: en effectuant avec soin une série de compromis entre des possibilités concurrentes. Les compilateurs et les outils sont conçus comme n’importe quel autre logiciel professionnel: ils décomposent le problème, écrivent une ligne de code à la fois, puis testent l’enfer du programme résultant.

La conception de la langue est un sujet énorme. Si vous souhaitez concevoir une langue, vous pouvez commencer par vous demander quelles sont les lacunes dans une langue que vous connaissez déjà. Les décisions de conception découlent souvent de la prise en compte d'un défaut de conception d'un autre produit.

Vous pouvez également envisager un domaine qui vous intéresse, puis concevoir un langage DSL (Domain-Specific Langage) spécifiant les solutions aux problèmes de ce domaine. Vous avez mentionné LOGO; c'est un excellent exemple de DSL pour le domaine du "dessin au trait". Les expressions régulières sont un DSL pour le domaine "trouver un modèle dans une chaîne". LINQ in C # / VB est un DSL pour le domaine "filtrer, joindre, trier et projeter des données". HTML est un DSL pour le domaine "décrire la mise en page du texte sur une page", etc. De nombreux domaines peuvent être résolus par des solutions linguistiques. Un de mes favoris est Inform7, qui est un DSL pour le domaine "jeu d'aventure à base de texte"; c'est probablement le langage de programmation sérieux de plus haut niveau que j'ai jamais vu.

Une fois que vous avez défini votre langue, essayez de noter avec précision les règles à suivre pour déterminer ce qu'est un programme légal et illégal. En règle générale, vous souhaiterez le faire à trois niveaux:

  1. lexical : quelles sont les règles pour les mots dans la langue, quels caractères sont légaux, à quoi ressemblent les chiffres, etc.
  2. syntaxique : comment les mots de la langue se combinent-ils en unités plus grandes? En C #, les grandes unités sont des expressions, des instructions, des méthodes, des classes, etc.
  3. sémantique : étant donné un programme juridique syntaxiquement, comment voulez - vous savoir ce que le programme fait ?

Notez ces règles aussi précisément que possible . Si vous faites un bon travail, vous pouvez vous en servir comme base pour écrire un compilateur ou un interprète. Regardez la spécification C # ou la spécification ECMAScript pour voir ce que je veux dire. ils regorgent de règles très précises qui décrivent ce qui fait un programme juridique et comment déterminer ce que l'on fait.

L'un des meilleurs moyens de commencer à écrire un compilateur consiste à écrire un compilateur de langage de haut niveau à langage de haut niveau . Ecrivez un compilateur qui prend des chaînes dans votre langue et les crache en C # ou en JavaScript ou quelle que soit la langue que vous connaissez; laissez le compilateur pour cette langue puis s'occuper de la lourde tâche de la transformer en code exécutable.

J'écris un blog sur la conception de C #, VB, VBScript, JavaScript et d'autres langages et outils; Si ce sujet vous intéresse, jetez-y un coup d'œil. http://blogs.msdn.com/ericlippert (historique) et http://ericlippert.com (actuel)

En particulier, vous pourriez trouver ce post intéressant; Ici, je liste la plupart des tâches que le compilateur C # effectue pour vous au cours de son analyse sémantique. Comme vous pouvez le constater, il y a beaucoup d'étapes. Nous divisons le gros problème d'analyse en une série de problèmes que nous pouvons résoudre individuellement.

http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx

Enfin, si vous êtes à la recherche d'un emploi dans ce domaine, envisagez de faire appel à Microsoft en tant que stagiaire et essayez de faire partie du groupe des développeurs. C'est comme ça que j'ai fini avec mon travail aujourd'hui!


Avez-vous écrit à quel point les optimisations du compilateur ne sont plus effectuées car le CLR peut les faire automatiquement?

6
@ Thorbjørn: Soyons clairs sur la terminologie. Un "compilateur" est un appareil qui traduit un langage de programmation en un autre. Un des avantages du compilateur C # qui transforme C # en IL et un compilateur IL (le "jitter") qui convertit IL en code machine est que vous devez écrire le compilateur C # en IL (facile!), Et mettre les optimisations spécifiques au processeur dans la gigue. Ce n'est pas que les optimisations du compilateur "ne sont pas faites", c'est que l'équipe du compilateur jit les fait pour nous. Voir blogs.msdn.com/b/ericlippert/archive/2009/06/11//
Eric Lippert le

6
@ Cyclotis04: Inform6 est compilé en Z-code, un exemple célèbre et extrêmement précoce d'une machine virtuelle à code-octet. C'est ainsi que tous les jeux Infocom des années 1980 pourraient être à la fois plus volumineux que la mémoire et portables pour plusieurs architectures; les jeux ont été compilés en z-code, puis des interpréteurs z-code avec pagination en mémoire de code ont été implémentés pour plusieurs machines. De nos jours, vous pouvez bien sûr faire appel à un interprète zcode sur une montre-bracelet, mais à l’époque, c’était de la haute technologie . Voir en.wikipedia.org/wiki/Z-machine pour plus de détails.
Eric Lippert

@EricLippert Compiler n'est pas un périphérique, mais un périphérique contient du matériel
Nous pouvons

2
@dhams: Un appareil est une chose faite pour un but particulier. Chaque compilateur que j'ai écrit a été exécuté sur du matériel spécialement conçu pour permettre aux compilateurs d'exister.
Eric Lippert

127

Vous pouvez trouver que Lets Build a Compiler de Jack Crenshaw est une introduction intéressante à la rédaction de compilateurs et au langage d’assemblage.

L'auteur est resté très simple et s'est concentré sur la création de fonctionnalités réelles.


2
Ce qui est intéressant dans l'intro de Crenshaw, c'est qu'elle se termine (spoiler: c'est incomplet) à peu près au même moment où vous foncez dans les problèmes qui vous feraient prendre conscience, hé, j'aurais vraiment dû concevoir mon langage avant de commencer à le mettre en œuvre. Et puis vous dites, hé, si je dois écrire une spécification de langage complet, pourquoi ne pas le faire dans une notation formelle que je peux ensuite alimenter dans un outil pour générer un analyseur syntaxique? Et puis vous le faites comme tout le monde.
kindall

3
@ kindall, vous devez l'avoir fait à la main pour comprendre qu'il y a une raison d'utiliser les outils.

72

"J'aimerais vraiment apprendre ce genre de choses". Si vous êtes sérieux à long terme:

  • Aller au collège, se spécialiser dans le génie logiciel. Prenez chaque classe de compilateur que vous pouvez obtenir. Les personnes qui fournissent les cours sont plus instruits et plus expérimentés que vous; C’est bien que leurs points de vue d’experts soient utilisés pour vous présenter les informations d’une manière que vous n’obtenez jamais en lisant du code.

  • Restez fidèle aux cours de mathématiques jusqu'au lycée et poursuivez vos études au collège pendant 4 ans. Concentrez-vous sur les mathématiques non standard: la logique, la théorie des groupes, la méta-mathématique. Cela vous obligera à penser de manière abstraite. Cela vous permettra de lire les articles théoriques avancés sur la compilation et de comprendre pourquoi ces théories sont intéressantes et utiles. Vous pouvez ignorer ces théories avancées si vous voulez toujours être à la pointe de la technologie.

  • Recueillez / lisez les textes standard du compilateur: Aho / Ullman, etc. Ils contiennent ce que la communauté reconnaît généralement comme étant fondamental. Vous pouvez ne pas utiliser tout ce qui est contenu dans ces livres, mais vous devez savoir qu’il existe et savoir pourquoi vous ne l’utilisez pas. Je pensais que Muchnick était génial, mais c’est pour des sujets assez avancés.

  • Construire un compilateur. Commencez MAINTENANT en construisant un pourri. Cela vous apprendra quelques problèmes. Construisez un deuxième. Répéter. Cette expérience crée une énorme synergie avec votre apprentissage du livre.

  • Un très bon point de départ est l’information sur BNF (Backus Naur Form), les analyseurs syntaxiques et les générateurs d’analyseurs syntaxiques. BNF est effectivement utilisé universellement dans les compilateurs, et vous ne pouvez pas parler de manière réaliste à vos collègues compilateurs du même type si vous ne le connaissez pas.

Si vous voulez une bonne première introduction à la compilation et la valeur directe de BNF non seulement pour la documentation, mais en tant que métalangage pouvant être traité par un outil, consultez ce tutoriel (et non le mien) sur la construction de compilateurs "méta" (compilateurs construisant des compilateurs) basés sur un article de 1964 (oui, vous avez bien lu) ["META II, un langage d’écriture pour compilateur orienté syntaxe" de Val Schorre. (http://doi.acm.org/10.1145/800257.808896)] Cet IMHO est l'un des meilleurs documents comp-sci jamais écrits: il vous apprend à construire des compilateurs-compilateurs en 10 pages. J'ai d'abord appris de cet article.

Ce que j’ai écrit plus haut découle de mon expérience personnelle et je pense que cela m’a assez bien servi. YMMV, mais à mon humble avis, pas de beaucoup.


54
-1 Aucune de ces réponses n'est nécessaire.
Neil Butterworth

77
@nbt Aucune de ces réponses n'est nécessaire. Mais tout ce qui précède aide. Vraiment beaucoup.
Konrad Rudolph le

1
Je suis particulièrement en désaccord avec le "Apprendre les maths à penser abstraitement!" suggestion. Même si vous pensez que "apprendre à penser abstraitement" est particulièrement utile pour créer votre propre langage de programmation et votre propre compilateur (je ne le trouve pas - je le trouve beaucoup plus utile d'apprendre par la pratique que par ces chemins détournés incroyablement indirects) , les mathématiques ne sont pas le seul domaine avec la pensée abstraite! (Je suis un mathématicien, donc je ne nie pas l'utilisation des mathématiques en général, juste son applicabilité dans ce cas particulier ...)
grautur

26
Si vous souhaitez lire les documents techniques avancés sur la théorie des compilateurs, vous devez être compétent en mathématiques. Vous pouvez décider d'ignorer cette littérature, et votre théorie et donc les compilateurs en seront plus pauvres. Les opposants ici soulignent tous que vous pouvez créer un compilateur sans beaucoup d'éducation formelle, et je suis d'accord. Ils semblent impliquer que vous pouvez construire de très bons compilateurs sans cela. Ce n'est pas un pari que je voudrais prendre.
Ira Baxter

7
CS est une discipline qui est vraiment utile pour la conception et la mise en œuvre du langage. Bien que ce ne soit pas obligatoire, bien sûr, mais des décennies de recherche peuvent et doivent être exploitées, et il n’ya aucune raison de répéter les autres erreurs.
Donal Fellows

46

Voici un livre / cours en ligne que vous pouvez suivre et intitulé Les éléments des systèmes informatiques: Construire un ordinateur moderne à partir de principes premiers .

En utilisant des simulateurs, vous construisez un système informatique complet à partir de la base. Alors que de nombreux commentateurs ont déclaré que votre question était trop large, ce livre y répond tout en restant très gérable. Lorsque vous avez terminé, vous avez écrit un jeu dans un langage de haut niveau (que vous avez conçu), qui utilise les fonctionnalités de votre propre système d'exploitation, compilées dans un langage de VM (que vous avez conçu) par votre compilateur. traduit dans un langage d'assemblage (que vous avez conçu) par votre traducteur VM, lequel est assemblé en code machine (que vous avez conçu) par votre assembleur, qui s'exécute sur votre système informatique que vous rassemblez à partir de puces que vous avez conçues à l'aide de la logique booléenne et un langage de description de matériel simple.

Les chapitres:

  1. Aperçu du cours
  2. Logique Booléenne
  3. Puces combinatoires
  4. Puces séquentielles
  5. Language de machine
  6. L'architecture des ordinateurs
  7. Assembleur
  8. Machine virtuelle I: arithmétique
  9. Machine virtuelle II: contrôle
  10. Langage de programmation
  11. Compilateur I: Analyse de syntaxe
  12. Compilateur II: Génération de code
  13. Système opérateur
  14. Élément de liste

Plus de plaisir à aller


Merci pour les modifications, personne inconnue. J'ai essayé plusieurs fois mais je ne pouvais pas concentrer mes pensées suffisamment pour la description ... mais je ne voulais pas ne pas mentionner le livre. Le livre est maintenant en ligne sur le lien du plan d’étude: www1.idc.ac.il/tecs/plan.html . C'est aussi un prix très raisonnable en ligne. Profitez de tout le monde.
Joe Internet

J'allais suggérer cela moi-même ... pour les fainéants, consultez l'intro de 10 minutes: De NAND à Tetris en 12 étapes @ youtube.com/watch?v=JtXvUoPx4Qs
Richard Anthony Hein

46

Prendre du recul. Un compilateur est simplement un programme qui traduit un document d'une langue en un document d'une autre langue. Les deux langues doivent être bien définies et spécifiques.

Les langages ne doivent pas nécessairement être des langages de programmation. Ils peuvent être n'importe quelle langue dont les règles peuvent être écrites. Vous avez probablement déjà vu Google Translate . c'est un compilateur car il peut traduire une langue (par exemple l'allemand) dans une autre (le japonais peut-être).

Un autre exemple de compilateur est un moteur de rendu HTML. Son entrée est un fichier HTML et la sortie est une suite d'instructions permettant de dessiner les pixels à l'écran.

Lorsque la plupart des gens parlent de compilateur, ils font généralement référence à un programme qui convertit un langage de programmation de haut niveau (tel que Java, C, Prolog) en un langage de bas niveau (assembleur ou code machine). Cela peut être intimidant. Mais ce n’est pas si mal quand on considère le généraliste qu’un compilateur est un programme qui traduit une langue dans une autre.

Pouvez-vous écrire un programme qui inverse chaque mot d'une chaîne? Par exemple:

When the cat's away, the mice will play.

devient

nehW eht s'tac yawa, eht ecim lliw yalp.

Ce n'est pas un programme difficile à écrire, mais vous devez penser à certaines choses:

  • Qu'est-ce qu'un "mot"? Pouvez-vous définir quels personnages composent un mot?
  • Où commencent et finissent les mots?
  • Les mots sont-ils séparés par un seul espace ou peut-il y en avoir plus - ou moins?
  • La ponctuation doit-elle aussi être inversée?
  • Qu'en est-il de la ponctuation dans un mot?
  • Qu'advient-il des lettres majuscules?

Les réponses à ces questions aident la langue à être bien définie. Maintenant, allez-y et écrivez le programme. Félicitations, vous venez d'écrire un compilateur.

Que diriez-vous de cela: pouvez-vous écrire un programme qui prend une série d’instructions de dessin et génère un fichier PNG (ou JPEG)? Peut-être quelque chose comme ça:

image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99

Encore une fois, vous devrez réfléchir pour définir le langage:

  • Quelles sont les instructions primitives?
  • Qu'est-ce qui vient après le mot "ligne"? Qu'est-ce qui vient après "couleur"? De même pour "background", "box", etc.
  • Qu'est ce qu'un numéro?
  • Un fichier d'entrée vide est-il autorisé?
  • Est-ce correct de capitaliser les mots?
  • Les nombres négatifs sont-ils autorisés?
  • Que se passe-t-il si vous ne donnez pas la directive "image"?
  • Est-il possible de ne pas spécifier de couleur?

Bien sûr, il y a plus de questions à répondre, mais si vous pouvez les préciser, vous avez défini un langage. Le programme que vous écrivez pour faire la traduction est, vous le devinez, un compilateur.

Vous voyez, écrire un compilateur n'est pas si difficile. Les compilateurs que vous avez utilisés en Java ou en C ne sont que des versions plus grosses de ces deux exemples. Alors allez-y! Définissez un langage simple et écrivez un programme pour que ce langage fasse quelque chose. Tôt ou tard, vous allez vouloir élargir votre langage. Par exemple, vous pouvez ajouter des variables ou des expressions arithmétiques. Votre compilateur deviendra plus complexe, mais vous comprendrez tout parce que vous l’avez écrit vous-même. C'est ainsi que les langages et les compilateurs sont créés.


7
myFirstCompiler = (str) -> ("" + (str || "")). split (''). reverse (). join (''); jsfiddle.net/L7qSr
Larry Battle

21

Si vous êtes intéressé par la conception d'un compilateur, consultez le Livre du Dragon (titre officiel: Compilateurs: principes, techniques et outils). Il est largement considéré comme un livre classique sur ce sujet.


4
Notez que vous aurez peut-être besoin d'une expérience un peu plus réelle pour tirer le meilleur parti de ce livre. Grande référence, cependant.

13
-1 Seul celui qui ne l'a pas lu peut penser que le livre de dragon est bon. et cela ne règle pas la question en particulier.
Neil Butterworth

33
Le livre du dragon? Pour un enthousiaste de quinze ans? Je préférerais qu'il garde son enthousiasme un peu plus longtemps.
David Thornley

1
Une alternative plus accessible: «Pragmatique du langage de programmation» 3e .
willjcroz

@DavidThornley Ne le comptez pas complètement (oui, je réalise que c'est un très vieux billet). J'ai commencé à étudier le fonctionnement des langues à l'âge de 15 ans et plus particulièrement aux machines virtuelles. Maintenant, j'ai 16 ans et après des mois de recherche, d'écriture et de réécriture, j'ai un interprète et un compilateur en état de travail qui me satisfait.
David


10

Ne croyez pas qu'il y ait quelque chose de magique dans un compilateur ou un système d'exploitation: il n'y en a pas. Vous vous souvenez des programmes que vous avez écrits pour compter toutes les voyelles d'une chaîne ou pour additionner les nombres d'un tableau? Un compilateur n'est pas différent dans son concept; c'est juste beaucoup plus grand.

Chaque programme comporte trois phases:

  1. lire des trucs
  2. traiter ce genre de choses: traduire les données d'entrée en données de sortie
  3. écrire d'autres choses - les données de sortie

Pensez-y: qu'est-ce que l'entrée dans le compilateur? Une chaîne de caractères d'un fichier source.

Quelle est la sortie du compilateur? Chaîne d'octets représentant les instructions de la machine à l'ordinateur cible.

Alors, quelle est la phase "processus" du compilateur? Que fait cette phase?

Si vous considérez que le compilateur - comme tout autre programme - doit inclure ces trois phases, vous aurez une bonne idée de la construction d'un compilateur.


3
Comme Neil l'a dit, vrai mais pas utile. Les aspects fondamentaux du compilateur, tels que la grammaire récursive et les tables de symboles, ne sont pas intuitifs.
Mason Wheeler

1
@Mason Wheeler: Je pense que quiconque aspire de manière réaliste à écrire un compilateur (et à concevoir le langage cible?) Est susceptible de penser que la grammaire et les tables de symboles récursives sont plutôt des concepts de base.
FumbleFingers

8

Je ne suis pas un expert, mais voici mon coup de couteau:

Vous ne semblez pas demander à écrire un compilateur, mais un assembleur. Ce n'est pas vraiment magique.

Voler quelqu'un d'autre à SO ( https://stackoverflow.com/questions/3826692/how-do-i-translate-assembly-to-binary ), l'assemblage ressemble à ceci:

label:  LDA #$00
        JMP label

Ensuite, vous le passez par un assembleur, et vous vous transformez en quelque chose comme ceci:

$A9 $00
$4C $10 $00

Seulement tout est écrasé, comme ceci:

$A9 $00 $4C $10 $00

Ce n'est vraiment pas magique.

Vous ne pouvez pas écrire cela dans le bloc-notes, car celui-ci utilise ASCII (pas hexadécimal). Vous utiliseriez un éditeur hexadécimal ou écrivez simplement les octets par programmation. Vous écrivez cet hex dans un fichier, nommez-le "a.exe" ou "a.out", puis dites au système d'exploitation de l'exécuter.

Bien sûr, les processeurs et les systèmes d'exploitation modernes sont très compliqués, mais c'est l'idée de base.

Si vous voulez écrire un nouveau compilateur, voici comment procéder:

1) Ecrivez un langage interprété en utilisant quelque chose comme l’exemple de calculatrice dans pyparsing (ou tout autre bon framework d’analyse syntaxique). Cela vous permettra de vous familiariser avec les bases de l'analyse.

2) Écrivez un traducteur. Traduisez votre langue en Javascript, par exemple. Maintenant, votre langue s'exécutera dans un navigateur.

3) Écrivez un traducteur à un niveau inférieur, comme LLVM, C ou Assembly.

Vous pouvez vous arrêter ici, c'est un compilateur. Ce n'est pas un compilateur d'optimisation, mais ce n'était pas la question. Vous devrez peut-être aussi envisager d’écrire un éditeur de liens et un assembleur, mais voulez-vous vraiment le faire?

4) (Insane) Écrivez un optimiseur. Les grandes équipes travaillent depuis des décennies sur ce sujet.

4) (Sane) Impliquez-vous dans une communauté existante. GCC, LLVM, PyPy, l’équipe principale travaillant sur tous les interprètes.


8

Plusieurs autres ont donné d'excellentes réponses. Je vais juste ajouter quelques suggestions supplémentaires. Premièrement, un bon livre pour ce que vous essayez de faire est le texte de mise en œuvre du compilateur moderne d’Appel (faites votre choix parmi C , Java ou Standard ML ). Ce livre décrit l’implémentation complète d’un compilateur pour un langage simple, Tiger, dans un assemblage MIPS pouvant être exécuté dans un émulateur, avec une bibliothèque de support d’exécution minimale. Pour un seul passage à travers tout le nécessaire pour faire fonctionner un langage compilé, c'est un très bon livre 1 .

Appel vous expliquera comment compiler une langue prédéfinie, mais ne passe pas beaucoup de temps sur la signification des différentes fonctionnalités linguistiques ni sur la façon de les considérer en fonction de leurs mérites relatifs pour concevoir les vôtres. Pour cet aspect, Langages de programmation: Concepts et constructions est correct. Concepts, techniques et modèles de programmation informatique est également un bon livre pour approfondir la conception du langage, bien qu'il le fasse dans le contexte d'un seul langage ( Oz ).

Enfin, j'ai mentionné qu'Appel avait son texte en C, Java et Standard ML - si vous êtes sérieux au sujet de la construction du compilateur et des langages de programmation, je vous recommande d'apprendre ML et d'utiliser cette version d'Appel. Les langages de la famille ML ont des systèmes de types forts qui sont principalement fonctionnels - des fonctionnalités qui seront différentes de beaucoup d'autres langages, donc les apprendre si vous ne connaissez pas déjà un langage fonctionnel perfectionnera votre langage. En outre, leurs schémas de correspondance de motif et fonctionnels conviennent parfaitement aux types de manipulations que vous devez souvent effectuer dans un compilateur. Les compilateurs écrits dans des langages basés sur ML sont généralement beaucoup plus courts et plus faciles à comprendre que les compilateurs écrits en C, Java, ou des langages similaires. Livre de Harpersur Standard ML est un très bon guide pour vous aider à démarrer; Travailler à travers cela devrait vous préparer à affronter le livre d’application du compilateur Standard ML d’Appel. Si vous apprenez la norme ML, il sera également assez facile de prendre OCaml pour un travail ultérieur; IMO offre de meilleurs outils au programmeur qui travaille (s'intègre plus proprement à l'environnement OS, crée des programmes exécutables facilement et possède des outils de construction de compilateur spectaculaires comme ulex et Menhir).


1 Pour les références à long terme, je préfère le Dragon Book, car il contient plus de détails sur les éléments auxquels je vais probablement faire référence, tels que le fonctionnement interne des algorithmes d'analyse syntaxique et couvre plus largement différentes approches, mais le livre d'Appel est très bon. pour un premier passage. Fondamentalement, Appel vous enseigne une façon de faire les choses tout au long du compilateur et vous guide à travers. Le livre du dragon aborde plus en détail différentes variantes de conception, mais fournit beaucoup moins de conseils sur la manière de faire fonctionner quelque chose.


Modifié : remplacez la référence Aho incorrecte par Sethi, mentionnez CTMCP.


Ugh, j'avais l'essentiel des langages de programmation pour mon cours d'interprètes de collège. C'était horrible. J'aime même les schémas personnellement et la syntaxe ne me dérange pas, ce sont les auteurs qui ont mal expliqué les concepts qui les ont ruinés pour moi.
Greg Guida

J'aime la compilation d'Appel avec continuations, mais j'ai trouvé que ses livres supposaient beaucoup de connaissances préalables.
Jon Harrop

6

Je devais créer un compilateur pour la classe au collège.

Ce n’est pas aussi compliqué que vous le pensiez. La première étape consiste à créer votre grammaire. Pensez à la grammaire anglaise. De la même manière, vous pouvez analyser une phrase si elle a un sujet et un prédicat. Pour en savoir plus sur les Grammars sans contexte .

Une fois que vous avez maîtrisé la grammaire (les règles de votre langue), écrire un compilateur est aussi simple que de simplement suivre ces règles. Les compilateurs traduisent généralement dans le code machine, mais à moins que vous ne vouliez apprendre le x86, je vous suggère peut-être de regarder MIPS ou de créer votre propre machine virtuelle.

Les compilateurs ont généralement deux parties, un scanner et un analyseur. Fondamentalement, le scanner lit le code et le sépare en jetons. L'analyseur examine la structure de ces jetons. Ensuite, le compilateur suit et suit des règles assez simples pour le convertir en un code quelconque (assembleur, code intermédiaire comme un code intermédiaire, etc.). Si vous le divisez en morceaux de plus en plus petits, cela ne sera finalement pas du tout décourageant.

Bonne chance!


8
Conceptuellement simple? Oui. En fait simple? N °
Neil Butterworth

7
Uhm. Après analyse / analyse, le compilateur doit effectuer une vérification de type / inférence, une optimisation, une allocation de registre, etc., etc. Ces étapes sont tout sauf simples. (Lorsque vous utilisez un code interprété, vous ne faites que reporter ces parties à l'étape d'exécution.)
Macke

Pas de réaction de ma part: bien que les compilateurs aient deux parties fondamentales, l’une d’elles consiste à créer une description abstraite du programme (généralement décomposée en analyse et analyse) et l’autre à écrire une version de cette description abstraite dans certains autre forme (par exemple, code machine). (Note secondaires: L' optimisation des compilateurs tentent généralement d'améliorer la description abstraite avant de l' écrire, mais c'est un raffinement.)
Donal Fellows

6

Le livre Code de Petzold est une excellente introduction aux non-techniciens comme aux techniciens en commençant par les principes de base. Il est très lisible et vaste, sans s’embourber.

Maintenant que j'ai écrit ceci, je vais devoir le relire.



5

Il y a d'excellentes réponses dans ce fil, mais je voulais juste ajouter les miennes, car moi aussi j'avais déjà eu la même question. (De plus, j'aimerais souligner que le livre suggéré par Joe-Internet est une excellente ressource.)

Tout d'abord, comment fonctionne un ordinateur? Voici comment: Entrée -> Calculer -> Sortie.

Considérons d’abord la partie «Calculer». Nous verrons plus tard comment fonctionnent les entrées et les sorties.

Un ordinateur consiste essentiellement en un processeur (ou CPU) et de la mémoire (ou RAM). La mémoire est un ensemble d'emplacements pouvant chacun stocker un nombre fini de bits. Chacun de ces emplacements mémoire peut lui-même être référencé par un numéro. C'est ce qu'on appelle l'adresse de l'emplacement mémoire. Le processeur est un gadget qui permet d'extraire des données. À partir de la mémoire, effectuez certaines opérations en fonction des données et réécrivez certaines données dans la mémoire. Comment le processeur détermine-t-il ce qu'il faut lire et quoi faire après avoir lu les données de la mémoire?

Pour répondre à cela, nous devons comprendre la structure d'un processeur. Ce qui suit est une vue assez simple. Un processeur est essentiellement composé de deux parties. L'un est un ensemble d'emplacements de mémoire construits à l'intérieur du processeur qui servent de mémoire de travail. Celles-ci s'appellent des «registres». Le second est un ensemble de machines électroniques construites pour effectuer certaines opérations en utilisant les données des registres. Il existe deux registres spéciaux appelés le "Compteur de programme" ou le PC et le "Registre d’instructions" ou l’IR. Le processeur considère que la mémoire est divisée en trois parties. La première partie est la «mémoire programme», qui stocke le programme informatique en cours d'exécution. La seconde est la "mémoire de données". Le troisième est utilisé à des fins spéciales, nous en reparlerons plus tard. Le compteur de programme contient l'emplacement de la prochaine instruction à lire dans la mémoire de programme. Le compteur d'instructions contient un nombre qui fait référence à l'opération en cours d'exécution. Chaque opération qu'un processeur peut effectuer est désignée par un numéro appelé code d'opération de l'opération. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Le compteur d'instructions contient un nombre qui fait référence à l'opération en cours d'exécution. Chaque opération pouvant être exécutée par un processeur est désignée par un numéro appelé code d'opération de l'opération. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Le compteur d'instructions contient un nombre qui fait référence à l'opération en cours d'exécution. Chaque opération pouvant être exécutée par un processeur est désignée par un numéro appelé code d'opération de l'opération. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Chaque opération pouvant être exécutée par un processeur est désignée par un numéro appelé code d'opération de l'opération. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Chaque opération pouvant être exécutée par un processeur est désignée par un numéro appelé code d'opération de l'opération. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre. Le fonctionnement d’un ordinateur consiste essentiellement à lire l’emplacement de mémoire référencé par le compteur de programme dans le registre d’instructions (et à incrémenter le compteur de programme afin qu’il pointe vers l’emplacement de mémoire de l’instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction peut consister à lire un emplacement de mémoire spécifique dans un registre, à écrire dans un registre ou à effectuer une opération en utilisant les valeurs de deux registres et écrire la sortie dans un troisième registre.

Maintenant, comment l'ordinateur effectue-t-il les entrées / sorties? Je vais fournir une réponse très simplifiée. Voir http://en.wikipedia.org/wiki/Input/output et http://en.wikipedia.org/wiki/Interrupt. pour plus. Il utilise deux choses, cette troisième partie de la mémoire et ce qu'on appelle des interruptions. Chaque périphérique connecté à un ordinateur doit pouvoir échanger des données avec le processeur. Pour ce faire, il utilise la troisième partie de la mémoire mentionnée précédemment. Le processeur alloue une tranche de mémoire à chaque périphérique et le périphérique et le processeur communiquent via cette tranche de mémoire. Mais comment le processeur sait-il à quel emplacement se réfère quel appareil et quand un appareil doit-il échanger des données? C’est là que les interruptions entrent en jeu. Une interruption est essentiellement un signal adressé au processeur pour mettre en pause ce qu’il est actuellement, sauvegarder tous ses registres dans un emplacement connu, puis commencer à faire autre chose. Il y a beaucoup d'interruptions, chacune est identifiée par un numéro unique. Un programme spécial est associé à chaque interruption. Lorsque l'interruption se produit, le processeur exécute le programme correspondant à l'interruption. Désormais, selon le bios et la façon dont les périphériques matériels sont connectés à la carte mère de l'ordinateur, chaque périphérique reçoit une interruption unique et une tranche de mémoire. Lors du démarrage du système d'exploitation à l'aide du bios, détermine l'interruption et l'emplacement de la mémoire de chaque périphérique et configure les programmes spéciaux permettant à l'interruption de gérer correctement les périphériques. Ainsi, lorsqu'un appareil a besoin de données ou veut envoyer des données, une interruption est signalée. Le processeur met en pause ce qu'il fait, gère l'interruption puis revient à ce qu'il fait. Il existe de nombreux types d’interruptions, tels que le disque dur, le clavier, etc. Un élément important est la minuterie du système, qui appelle une interruption à intervalles réguliers. Il existe également des codes opération pouvant déclencher des interruptions, appelées interruptions logicielles.

Maintenant, nous pouvons presque comprendre le fonctionnement d’un système d’exploitation. Lors du démarrage, l’OS configure l’interruption de la minuterie de manière à lui donner le contrôle à intervalles réguliers. Il configure également d'autres interruptions pour gérer d'autres périphériques, etc. Maintenant, lorsque l'ordinateur exécute une multitude de programmes et que l'interruption du minuteur se produit, l'OS acquiert le contrôle et effectue des tâches importantes telles que la gestion des processus, la gestion de la mémoire, etc. un moyen abstrait pour les programmes d'accéder aux périphériques matériels, plutôt que de les laisser accéder directement aux périphériques. Lorsqu'un programme souhaite accéder à un périphérique, il appelle du code fourni par le système d'exploitation qui communique ensuite avec le périphérique. Il y a beaucoup de théorie impliquée dans ceux-ci qui traite de la simultanéité, des threads, des verrous, de la gestion de la mémoire, etc.

Maintenant, on peut en théorie écrire un programme directement en utilisant des opcodes. C'est ce qu'on appelle le code machine. C'est évidemment très douloureux. Désormais, un langage d'assemblage pour le processeur n'est rien d'autre que des mnémoniques pour ces opcodes, ce qui facilite l'écriture de programmes. Un assembleur simple est un programme qui prend un programme écrit en assembleur et remplace les mnémoniques par les opcodes appropriés.

Comment fait-on pour concevoir un processeur et un langage d'assemblage. Pour savoir que vous devez lire quelques livres sur l'architecture informatique. (voir les chapitres 1 à 7 du livre cité par joe-internet). Cela implique d'apprendre à propos de l'algèbre booléenne, comment construire des circuits combinatoires simples pour ajouter, multiplier, etc., comment construire de la mémoire et des circuits séquentiels, comment construire un microprocesseur, etc.

Maintenant, comment écrit-on des langues informatiques? On pourrait commencer par écrire un assembleur simple en code machine. Utilisez ensuite cet assembleur pour écrire un compilateur pour un simple sous-ensemble de C. Ensuite, utilisez ce sous-ensemble de C pour écrire une version plus complète de C. Enfin, utilisez C pour écrire un langage plus complexe, tel que python ou C ++. Bien sûr, pour écrire une langue, vous devez d’abord la concevoir (de la même manière que vous utilisez un processeur). Encore une fois, regardez quelques manuels à ce sujet.

Et comment écrit-on un os. D'abord, vous ciblez une plate-forme telle que x86. Ensuite, vous déterminez comment il démarre et quand votre système d’exploitation sera invoqué. Un pc typique démarre de cette façon. Il démarre et le bios effectue des tests. Ensuite, le bios lit le premier secteur du disque dur et charge le contenu dans un emplacement spécifique de la mémoire. Ensuite, il configure le cpu pour commencer à exécuter les données chargées. C'est le point où vous êtes appelé. Un système d'exploitation typique à ce stade charge la mémoire restante. Ensuite, il initialise les périphériques et configure d'autres éléments, puis vous accueille avec l'écran de connexion.

Donc, pour écrire un os, vous devez écrire le "chargeur de démarrage". Ensuite, vous devez écrire du code pour gérer les interruptions et les périphériques. Ensuite, vous devez écrire tout le code pour la gestion des processus, la gestion des périphériques, etc. Ensuite, vous devez écrire une API permettant aux programmes exécutés dans votre système d’accéder aux périphériques et autres ressources. Et enfin, vous devez écrire du code qui lit un programme à partir du disque, le configure en tant que processus et commence à l'exécuter.

Bien entendu, ma réponse est ouvertement simplifiée et probablement peu utile dans la pratique. Pour ma défense, je suis maintenant un étudiant diplômé en théorie, alors j'ai oublié beaucoup de ces choses. Mais vous pouvez rechercher beaucoup de ces choses sur Google et en savoir plus.


4

Je me souviens d'un moment dans ma carrière de programmeur où j'étais dans un état de confusion similaire à la vôtre: j'avais beaucoup lu sur la théorie, le livre Dragon, le livre Tigre (en rouge), mais je n'en avais toujours pas beaucoup un indice comment mettre tout cela ensemble.

Ce qui l’a uni, c’est de trouver un projet concret à faire (puis de découvrir que je n’avais besoin que d’un petit sous-ensemble de la théorie).

La machine virtuelle Java m’a fourni un bon point de départ: c’est un "processeur" conceptuel, mais elle est très abstraite des détails désordonnés des processeurs actuels. Cela constitue également une partie importante et souvent négligée du processus d’apprentissage: démonter les objets avant de les rassembler (comme les enfants le faisaient autrefois avec des postes de radio).

Jouez avec un décompilateur et la classe Hello, World en Java. Lisez les spécifications de la JVM et essayez de comprendre ce qui se passe. Cela vous donnera un aperçu de la terre dans tout ce que le compilateur est en train de faire .

Puis jouez avec le code qui crée la classe Hello, World. (En réalité, vous créez un compilateur spécifique à l'application, pour un langage hautement spécialisé dans lequel vous ne pouvez dire que Hello, World.)

Essayez d’écrire du code capable de lire Hello, World écrit dans une autre langue et d’afficher la même classe. Faites en sorte que vous puissiez changer la chaîne de "Hello, World" à quelque chose d'autre.

Maintenant, essayez de compiler (en Java) une classe qui calcule une expression arithmétique, comme "2 * (3 + 4)". Prenez cette classe à part, écrivez un "compilateur de jouets" qui puisse la rassembler.


3

1) Grandes conférences vidéo de l'Université de Washington:

Construction du compilateur CSE P 501 - Automne 2009 www.cs.washington.edu/education/courses/csep501/09au/lectures/video.html *

2) SICP http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/ Et le livre du même nom. Il s’agit en fait d’une obligation pour tout ingénieur logiciel.

3) Également, concernant la programmation fonctionnelle, Haskell, le calcul lambda, la sémantique (y compris dénotationnelle) et la mise en œuvre du compilateur pour les langages fonctionnels. Vous pouvez commencer à partir de 2005-SS-FP.V10.2005-05-24.HDV si vous connaissez déjà Haskell. Les vidéos Uxx sont des réponses. Suivez d' abord les vidéos Vxx .

http://video.s-inf.de/#FP.2005-SS-Giesl.(COt).HD_Videoaufzeichnung

(Les vidéos sont en anglais, les autres cours sont en allemand cependant.)

  • les nouveaux utilisateurs ne peuvent publier qu’un maximum de deux hyperliens.

3

ANTLR est un bon point de départ. C'est un cadre générateur de langage, similaire à Lex et Yacc. Il existe une interface graphique appelée ANTLRWorks qui simplifie le processus.

Dans le monde .NET, il existe le Dynamic Language Runtime qui peut être utilisé pour générer du code dans le monde .NET. J'ai écrit un langage d'expression appelé Zentrum qui génère du code à l'aide du DLR. Il vous montrera comment analyser et exécuter des expressions typées de manière statique et dynamique.


2

Pour une introduction simple au fonctionnement des compilateurs et à la création de votre propre langage de programmation, je vous recommande le nouveau livre http://createyourproglang.com qui se concentre davantage sur la théorie de la conception de langage sans avoir à connaître les éléments internes OS / CPU, à savoir lexers, analyseurs syntaxiques , interprètes, etc.

Il utilise les mêmes outils qui ont été utilisés pour créer les langages de programmation Coffee Script et Fancy récemment populaires .


2

Si tout ce que vous dites est vrai, vous avez le profil d'un chercheur prometteur et une compréhension concrète ne peut être obtenue que dans un sens: étudier. Et je ne dis pas " Lisez tous ces livres de science informatique de haut niveau (spécialement ceux-ci ) écrits par ce génie !"; Je veux dire: il faut être avec des gens de haut niveau pour être un informaticien comme Charles Babbage, Alan Turing, Claude Shannon ou Dennis Ritchie. Je ne méprise pas les autodidactes (j'en suis un), mais il n'y a pas beaucoup de gens comme vous là-bas. Je recommande sérieusement Symbolic Systems Program (SSP) à l'Université de Stanford . Comme leur site Web le dit:

Le programme des systèmes symboliques (SSP) de l’Université de Stanford est axé sur les ordinateurs et les esprits: systèmes artificiels et naturels qui utilisent des symboles pour représenter des informations. SSP réunit des étudiants et des professeurs intéressés par différents aspects de la relation homme-ordinateur, notamment ...

  • sciences cognitives : étudier l'intelligence humaine, les langages naturels et le cerveau en tant que processus informatiques;
  • intelligence artificielle : doter les ordinateurs d'un comportement et d'une compréhension semblables à ceux de l'homme; et
  • interaction homme-machine : conception de logiciels et d’interfaces fonctionnant bien avec les utilisateurs.

2

Je vais suggérer quelque chose un peu en dehors du champ gauche: apprendre Python (ou peut-être Ruby, mais j'ai beaucoup plus d'expérience en Python, c'est ce dont je vais parler). Et pas seulement y barboter, mais vraiment apprendre à le connaître à un niveau profond.

Je suggère ceci pour plusieurs raisons:

  1. Python est un langage exceptionnellement bien conçu. Bien qu'il ait quelques verrues, il a moins de IMHO que beaucoup d'autres langues. Si vous êtes un concepteur de langues en herbe, il est bon de vous exposer au plus grand nombre possible de bonnes langues.

  2. L'implémentation standard de Python (CPython) est open-source et bien documentée, ce qui facilite la compréhension du fonctionnement du langage sous le capot.

  3. Python est compilé en un simple code octet qui est plus facile à comprendre que l’assemblage et qui fonctionne de la même manière sur toutes les plateformes sur lesquelles Python est exécuté. Ainsi, vous en apprendrez davantage sur la compilation (puisque Python compile votre code source en code octet) et sur l'interprétation (car ce code octet est interprété dans la machine virtuelle Python).

  4. Python propose de nombreuses nouvelles fonctionnalités, documentées dans des PEP (propositions d’amélioration Python) numérotées. Il est intéressant de lire les PPE pour voir comment les concepteurs de langage ont envisagé de mettre en œuvre une fonctionnalité avant de choisir la manière dont ils l’ont réellement réalisée. (Les PPE encore à l'étude sont particulièrement intéressantes à cet égard.)

  5. Python offre un mélange de fonctionnalités issues de divers paradigmes de programmation. Vous apprendrez ainsi différentes manières de résoudre les problèmes et disposerez d'un plus large éventail d'outils à inclure dans votre propre langage.

  6. Python facilite l’extension de la langue de différentes manières avec les décorateurs, les métaclasses, les points d’importation, etc., de sorte que vous puissiez jouer avec de nouvelles fonctionnalités linguistiques sans quitter réellement la langue. (En aparté: les blocs de code sont des objets de première classe dans Ruby, vous pouvez donc écrire de nouvelles structures de contrôle telles que des boucles! J'ai l'impression que les programmeurs de Ruby ne considèrent pas nécessairement l'extension du langage, vous programmez en Ruby, mais c’est plutôt cool.)

  7. En Python, vous pouvez réellement désassembler le bytecode généré par le compilateur, ou même écrire le vôtre à partir de rien et laisser l’interprète l’exécuter (je l’ai fait moi-même, et c’était hallucinant mais amusant).

  8. Python a de bonnes bibliothèques pour analyser. Vous pouvez analyser le code Python dans un arbre de syntaxe abstraite, puis le manipuler à l'aide du module AST. Le module PyParsing est utile pour analyser des langages arbitraires, tels que ceux que vous concevez. Vous pouvez théoriquement écrire votre compilateur de langue maternelle en Python si vous le souhaitez (et générer des sorties en C, en assembleur ou même en Python).

Cette approche d'investigation peut aller de pair avec une approche plus formelle, car vous allez commencer à reconnaître les concepts que vous avez étudiés dans la langue dans laquelle vous travaillez, et vice versa.

S'amuser!


Ne pas creuser au python, mais c'est à côté du point. L'enfant a déjà N langues pour N grand; incrémenter N ne fera pas beaucoup de différence. Prenez C, par exemple. C'est standard. Il y a beaucoup de bibliothèques. C'est multi-plateforme (quand on s'en tient à la norme). Vous pouvez démonter la sortie. Vous pouvez écrire CFront. Etc. Donc là.
Ian

1

Eh bien, je pense que votre question pourrait être reformulée de la manière suivante: "Quels sont les concepts de base d'un diplôme en informatique", et la réponse globale est, bien sûr, d'obtenir votre propre baccalauréat en informatique.

Fondamentalement, vous créez votre propre compilateur de langage de programmation en lisant un fichier texte, en en extrayant des informations et en effectuant des transformations sur le texte à partir des informations que vous avez lues, jusqu'à ce que vous les transformiez en octets pouvant être lus par le chargeur (cf, Linkers and Loaders de Levine). Un compilateur trivial est un projet assez rigoureux lorsqu'il est terminé pour la première fois.

Le noyau d'un système d'exploitation est le noyau, qui gère les ressources (par exemple, l'allocation / désallocation de mémoire) et bascule entre les tâches / processus / programmes.

Un assembleur est une transformation texte-> octet.

Si cela vous intéresse, je suggérerais d'écrire un assembleur X86, sous Linux, prenant en charge un sous-ensemble d'assemblages X86 standard. Ce sera un point d’entrée assez simple et vous introduira à ces questions. Ce n'est pas un projet de bébé et vous apprendra beaucoup de choses.

Je recommanderais de l'écrire en C; C est la lingua franca pour ce niveau de travail.


1
Par contre, c’est un bon endroit pour une langue de très haut niveau. Tant que vous pouvez dicter les octets individuels dans un fichier, vous pouvez créer un compilateur / assembleur (ce qui est plus facile) dans n'importe quelle langue. Dis, perl. Ou VBA. Cieux, les possibilités!
Ian

1

Voir le livre de Kenneth Louden, "Compiler Construction"

http://www.cs.sjsu.edu/~louden/cmptext/

Il fournit une meilleure approche pratique du développement du compilateur.

Les gens apprennent en faisant. Seul un petit nombre peut voir les symboles inscrits sur le tableau et passer immédiatement de la théorie à la pratique. Malheureusement, ces personnes sont souvent dogmatiques, fondamentalistes et les plus fortes à ce sujet.


1

J'ai eu la chance d'être exposé au PDP-8 comme premier langage d'assemblage. Le PDP-8 ne comportait que six instructions, si simples qu'il était facile de les imaginer mises en œuvre par quelques composants discrets, ce qu'ils étaient en réalité. Cela a vraiment enlevé la "magie" des ordinateurs.

Une autre passerelle vers la même révélation est le langage d'assemblage "mix" que Knuth utilise dans ses exemples. "Mix" semble archaïque aujourd'hui, mais il a toujours cet effet DE-mystifiant.


0

Les compilateurs et les langages de programmation (et tout ce qui en fait partie - comme la définition d’une grammaire finie et la conversion en assembleur) est une tâche très complexe qui nécessite une grande compréhension de l’ensemble des systèmes. Ce type de cours est généralement offert en université à la 3e / 4e année de Comp Sci.

Je vous recommande vivement de commencer par mieux comprendre les systèmes d'exploitation en général et la manière dont les langages existants sont compilés / exécutés (c'est-à-dire nativement (C / C ++), dans une VM (Java) ou par un interpréteur (Python / Javascript)).

Je crois que nous avons utilisé le livre Operating System Concepts d’Abraham Silberschatz, Peter B. Galvin et Greg Gagne dans mon cours sur les systèmes d’exploitation (en 2e année). C'était un excellent livre qui donnait un aperçu complet de chaque composant d'un système d'exploitation - un peu cher mais qui en valait la peine et des copies anciennes / usagées devraient flotter.


Concepts de système d'exploitation? Très peu de cela est nécessaire pour construire un compilateur. Ce qui est nécessaire, c'est une compréhension des architectures logicielles: adresse les espaces, les piles, les threads (s'il veut apprendre les compilateurs, il vaut mieux en apprendre plus sur le parallélisme, c'est son avenir).
Ira Baxter

Immédiatement après avoir déclaré qu'il souhaitait apprendre le langage et les compilateurs, il a déclaré vouloir en savoir plus sur les systèmes d'exploitation.
David Thornley

@Ira - d'accord. Je n'ai jamais dit qu'il était nécessaire de comprendre le système d'exploitation pour construire un compilateur / langage, j'ai simplement expliqué que cela pourrait être un point de départ plus facile. Tout le monde se concentre sur l'aspect "compilateur" de sa question mais il a également indiqué qu'il souhaitait une meilleure compréhension des systèmes d'exploitation et des bibliothèques. Pour un jeune de 15 ans qui en apprend toujours davantage sur les architectures, il serait bien plus utile de comprendre la gestion de la mémoire, les threads, le verrouillage, les entrées / sorties, etc. que d’apprendre à définir une grammaire avec yacc (IMHO)
plafond

Désolé ... j'ai manqué le but de vouloir en savoir plus sur les systèmes d'exploitation. Mon argument est valable: il n’a pas besoin de beaucoup de connaissances sur le système d’exploitation pour les compilateurs. En fait, c'est un sujet complètement différent, sauf lorsque le compilateur et le système d'exploitation interagissent pour atteindre un objectif commun. (Par exemple, Multics a demandé à ses compilateurs PL / 1 de créer des appels de fonction de manière à activer une machine virtuelle globale).
Ira Baxter

0

C'est un sujet important, mais plutôt que de vous effacer par un pompeux "allez lire un livre, gamin", je me ferai un plaisir de vous donner des conseils pour vous aider à bien comprendre.

La plupart des compilateurs et / ou interprètes fonctionnent comme ceci:

Tokenize : Scannez le texte du code et divisez -le en une liste de jetons.

Cette étape peut être délicate car vous ne pouvez pas simplement diviser la chaîne d'espaces, vous devez reconnaître qu'il if (bar) foo += "a string";s'agit d'une liste de 8 jetons: WORD, OPEN_PAREN, WORD, CLOSE_PAREN, WORD, ASIGNMENT_ADD, STRING_LITERAL, TERMINATOR. Comme vous pouvez le constater, diviser simplement le code source en espaces ne fonctionnera pas. Vous devez lire chaque caractère sous forme de séquence. Ainsi, si vous rencontrez un caractère alphanumérique, continuez à lire les caractères jusqu'à ce que vous trouviez un caractère non alphanum et cette chaîne. Il suffit de lire est un mot à classer plus tard. Vous pouvez décider vous-même de la granularité de votre tokenizer: soit-il avalera-t-il "a string"sous la forme d'un jeton appelé STRING_LITERAL pour être analysé plus tard, ou s'il verra"a string" comme OPEN_QUOTE, UNPARSED_TEXT, CLOSE_QUOTE ou autre, il ne s'agit que de l'un des nombreux choix que vous devez choisir vous-même au moment de le coder.

Lex : Alors maintenant vous avez une liste de jetons. Vous avez probablement étiqueté des jetons avec une classification ambiguë, telle que WORD, car lors du premier passage, vous ne passez pas trop de temps à essayer de comprendre le contexte de chaque chaîne de caractères. Alors maintenant, relisez votre liste de jetons sources et reclassifiez chacun des jetons ambigus avec un type de jeton plus spécifique en fonction des mots-clés de votre langue. Donc, vous avez un mot tel que "si", et "si" est dans votre liste de mots-clés spéciaux appelés symbole SI afin de changer le type de symbole de ce jeton de mot à mot, et tout mot qui ne figure pas dans votre liste de mots-clés spéciaux , comme WORD foo, est un IDENTIFIANT.

Parse : Alors maintenant, vous avez tourné if (bar) foo += "a string";une liste de jetons lexés qui ressemble à ceci: IF IDENTIFIANT OPEN_PAREN IDENTIFIANT CLOSE_PAREN ASIGN_ADD STRING_LITERAL TERMINATOR. L'étape consiste à reconnaître les séquences de jetons en tant qu'énoncés. Ceci est l'analyse. Vous faites cela en utilisant une grammaire telle que:

STATEMENT: = ASIGN_EXPRESSION | IF_STATEMENT

IF_STATEMENT: = IF, PAREN_EXPRESSION, STATEMENT

ASIGN_EXPRESSION: = IDENTIFIANT, ASIGN_OP, VALEUR

PAREN_EXPRESSSION: = OPEN_PAREN, VALUE, CLOSE_PAREN

VALEUR: = IDENTIFIANT | STRING_LITERAL | PAREN_EXPRESSION

ASIGN_OP: = EQUAL | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT

Les productions qui utilisent "|" entre les termes signifie "correspond à l'un de ces", s'il y a des virgules, il signifie "correspond à cette séquence de termes"

Comment utilisez-vous cela? En commençant par le premier jeton, essayez de faire correspondre votre séquence de jetons à ces productions. Donc, vous essayez d’abord de faire correspondre votre liste de jetons avec STATEMENT, de sorte que vous lisiez la règle pour STATEMENT et qu’elle indique "un STATEMENT est soit un ASIGN_EXPRESSION, soit un IF_STATEMENT". Vous essayez donc de faire correspondre ASIGN_EXPRESSION en premier. Vous devez donc rechercher la règle de grammaire de ASIGN_EXPRESSION. et il indique "ASIGN_EXPRESSION est un IDENTIFIANT suivi d'un ASIGN_OP suivi d'une VALEUR, de sorte que vous recherchez la règle de grammaire pour IDENTIFIER et que vous voyez qu'il n'y a pas de syntaxe grammaticale pour IDENTIFIER, ce qui signifie IDENTIFIANT un" terminal ", ce qui signifie qu'il ne nécessite pas davantage analyser pour le faire correspondre afin que vous puissiez le faire directement avec votre jeton, mais votre premier jeton source est un IF, et si IF n'est pas identique à un IDENTIFIANT, la correspondance a échoué. Et maintenant? Vous revenez à la règle STATEMENT et essayez de faire correspondre le terme suivant: IF_STATEMENT. Vous regardez IF_STATEMENT, il commence par IF, lookup IF, IF est un terminal, comparez le terminal avec votre premier jeton, des correspondances de jetons IF, continuez, le terme suivant est PAREN_EXPRESSION, recherchez PAREN_EXPRESSION, ce n'est pas un terminal. PAREN_EXPRESSION commence par OPEN_PAREN, recherchez OPEN_PAREN, c'est un terminal, faites correspondre OPEN_PAREN à votre prochain jeton, cela correspond, etc., etc.

La méthode la plus simple pour aborder cette étape consiste à utiliser une fonction appelée parse () à laquelle vous transmettez le jeton de code source que vous essayez de faire correspondre et le terme de grammaire avec lequel vous essayez de le faire correspondre. Si le terme de grammaire n'est pas un terminal, vous appelez ensuite parse () en lui renvoyant le même jeton source et le premier terme de cette règle de grammaire. C’est pourquoi on appelle cela un "analyseur de descente récursif". La fonction parse () renvoie (ou modifie) votre position actuelle lors de la lecture des jetons source, elle renvoie essentiellement le dernier jeton de la séquence correspondante et vous continuez le prochain appel à parse () à partir de là.

Chaque fois que parse () correspond à une production telle que ASIGN_EXPRESSION, vous créez une structure représentant ce morceau de code. Cette structure contient des références aux jetons source d'origine. Vous commencez à construire une liste de ces structures. Nous appellerons toute cette structure l'arbre de syntaxe abstraite (AST)

Compiler et / ou exécuter : pour certaines productions de votre grammaire, vous avez créé des fonctions de gestionnaire qui, si elles disposaient d'une structure AST, compileraient ou exécuteraient cette partie d'AST.

Alors regardons le morceau de votre AST qui a le type ASIGN_ADD. Donc, en tant qu'interprète, vous avez une fonction ASIGN_ADD_execute (). Cette fonction est transmise en tant que partie de l'AST correspondant à l'arborescence d'analyse pour foo += "a string". Cette fonction examine donc cette structure et sait que le premier terme de la structure doit être un IDENTIFIANT, et que le second terme est la VALEUR. ASIGN_ADD_execute () passe le terme VALUE à une fonction VALUE_eval () qui renvoie un objet représentant la valeur évaluée en mémoire, puis ASIGN_ADD_execute () effectue une recherche sur "foo" dans votre table de variables et stocke une référence à tout ce qui a été renvoyé par eval_value () une fonction.

C'est un interprète. Au lieu de cela, un compilateur aurait des fonctions de gestionnaire traduisant l'AST en code d'octet ou en code machine au lieu de l'exécuter.

Les étapes 1 à 3, et certaines 4, peuvent être facilitées avec des outils tels que Flex et Bison. (alias Lex et Yacc) mais écrire un interprète vous-même est probablement l'exercice le plus stimulant qu'un programmeur puisse réaliser. Tous les autres défis en matière de programmation semblent triviaux après le sommet de celui-ci.

Mon conseil est de commencer petit: une langue minuscule, avec une grammaire minuscule, et essayez d'analyser et d'exécuter quelques instructions simples, puis progressez à partir de là.

Lisez-les et bonne chance!

http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c

http://en.wikipedia.org/wiki/Recursive_descent_parser


2
Vous faites ce que je considère comme une erreur classique quand les gens pensent à la compilation: c’est croire que le problème est l’analyse. PARSING EST TECHNIQUEMENT FACILE; il existe d'excellentes technologies pour le faire. La partie difficile de la compilation est l’analyse sémantique, l’optimisation aux niveaux haut et bas de la représentation du programme et la génération de code, l’accent étant de plus en plus mis sur le code PARALLEL. Vous avez complètement banalisé cela dans votre réponse: "un compilateur aurait des fonctions de gestionnaire pour traduire l'AST en code d'octet". Il y a 50 années de théorie du compilateur et d'ingénierie qui s'y cachent.
Ira Baxter

0

Le domaine informatique n’est compliqué que parce qu’il a eu le temps d’évoluer dans de nombreuses directions. En son cœur, ce sont juste des machines qui calculent.

Mon ordinateur très basique préféré est l’ordinateur relais de Harry Porter . Cela donne une idée du fonctionnement d'un ordinateur au niveau de base. Ensuite, vous pouvez commencer à comprendre pourquoi des éléments tels que les langages et les systèmes d'exploitation sont nécessaires.

En réalité, il est difficile de comprendre quoi que ce soit sans comprendre ce qui en a besoin . Bonne chance et ne faites pas que lire des trucs. Faire des choses.



-1

Un autre bon livre d'introduction est "Compilerbau" de 1986 (construction du compilateur) de N. Wirth. Il compte environ 100 pages et explique un code concis et bien conçu pour le langage du jouet PL / 0, comprenant un analyseur syntaxique, un générateur de code et une machine virtuelle. Il montre également comment écrire un analyseur qui lit la grammaire à analyser en notation EBNF. Le livre est en allemand, mais j’ai rédigé un résumé et traduit le code en Python sous forme d’exercice (voir http://www.d12k.org/cmplr/w86/intro.html) .


-1

Si vous souhaitez comprendre l’essence des langages de programmation, je vous suggérerais de travailler avec le livre PLAI (http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/) pour comprendre les concepts et leur mise en œuvre. Cela vous aidera également à concevoir votre propre langue.


-1

Si vous avez vraiment un intérêt pour le compilateur et que vous ne l'avez jamais fait auparavant, vous pouvez commencer par concevoir une calculatrice pour calculer des formules arithmétiques (une sorte de DSL, comme le mentionnait Eric). Il y a plusieurs aspects à prendre en compte pour ce type de compilateur:

  • Numéros autorisés
  • Opérateurs autorisés
  • Les priorités de l'opérateur
  • Validation de la syntaxe
  • Mécanisme de recherche variable
  • Détection de cycle
  • Optimisation

Par exemple, vous avez les formules suivantes, votre calculatrice devrait pouvoir calculer la valeur de x:

a = 1
b = 2
c = a + b
d = (3 + b) * c
x = a - d / b

Ce n'est pas un compilateur extrêmement difficile au début, mais pourrait vous faire réfléchir davantage sur quelques idées de base de ce qu'est un compilateur, et également vous aider à améliorer vos compétences en programmation et à contrôler la qualité de votre code (c'est en fait un problème Développement piloté par les tests (TDD) pourrait s’appliquer pour améliorer la qualité du logiciel).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.