Maison >interface Web >js tutoriel >Écrire un interpréteur js en utilisant JavaScript

Écrire un interpréteur js en utilisant JavaScript

hzc
hzcavant
2020-07-02 09:33:023023parcourir

Utiliser js pour 编译 js semble être une chose haut de gamme, mais le principe réel est en fait très simple. Ce n'est rien de plus que de la magie noire réalisée en utilisant la fonctionnalité de js 对象属性可以用字符串表示. La raison pour laquelle
a cette apparence 深奥 est probablement parce que les tutoriels existants sur Internet vous donnent toujours un babylon / @babel/parser d'abord, laissez tout le monde voir une grande liste de AST, puis publiez une grande liste d'entre eux. ,
AST directement récursif pour traiter tous les types de nœuds Au final, les novices ont réussi à faire peur.

Le but de la rédaction de cet article aujourd'hui est de vous proposer un tutoriel js2js facile à comprendre et pouvant être compris même par ceux qui viennent d'apprendre js.

Jetons d'abord un coup d'œil à l'effet

Écrire un interpréteur js en utilisant JavaScript

Un interpréteur le plus simple

Comme mentionné ci-dessus, js a une fonctionnalité qui est对象属性可以用字符串表示, par exemple, console.log est équivalent à console['log'], donc sur la base de cette fonctionnalité, nous pouvons écrire un prototype extrêmement pauvre et grossier

  function callFunction(fun, arg) {

    this[fun](arg);

  }

  callFunction('alert', 'hello world');

  // 如果你是在浏览器环境的话,应该会弹出一个弹窗

Comme il s'agit d'une version simplifiée, elle doit être Il y a beaucoup de problèmes. La syntaxe en js ne concerne pas seulement les appels de fonction. Voyons comment l'affectation est implémentée à l'aide de la magie noire

  function declareVarible(key, value) {

    this[key] = value;

  }

  declareVarible.call(window, 'foo', 'bar');

  // window.foo = 'bar'

Const : const peut être implémenté en utilisant Object.defineProperty;

Si vous pouvez comprendre le code ci-dessus, cela signifie que vous comprenez déjà les principes de base de js 解释器. Si vous ne le comprenez pas, vous ne pouvez que m'en vouloir.

Renforcer légèrement

Comme vous pouvez le voir, pour plus de commodité ci-dessus, nous avons écrit l'appel de fonction comme callFunction('alert', 'hello world');, mais il ne ressemble pas du tout à js 解释器,
dans notre esprit L'interpréteur souhaité devrait au moins ressembler à ceci parse('alert("hello world")''), faisons donc une petite modification Ici, nous devons introduire babel,
mais ne vous inquiétez pas pour l'instant, l'arbre syntaxique (AST) que nous avons analysé est également très. simple.

import babelParser from '@babel/parser';

const code = 'alert("hello world!")';

const ast = babelParser.parse(code);

Le code ci-dessus analyse le contenu suivant

{
  "type": "Program",
  "start": 0,
  "end": 21,
  "body": [
    {
      "type": "ExpressionStatement",
      "start": 0,
      "end": 21,
      "expression": {
        "type": "CallExpression",
        "start": 0,
        "end": 21,
        "callee": {
          "type": "Identifier",
          "start": 0,
          "end": 5,
          "name": "alert"
        },
        "arguments": [
          {
            "type": "Literal",
            "start": 6,
            "end": 20,
            "value": "hello world!",
            "raw": "\"hello world!\""
          }
        ]
      }
    }
  ],
  "sourceType": "module"
}

Le contenu ci-dessus semble être important, mais ce que nous utilisons réellement n'est en fait qu'une petite partie. Simplifions-le un peu et économisons. les choses qui ne sont pas nécessaires pour le moment. Supprimons les champs qui arrivent en premier

{
  "type": "Program",
  "body": [
    {
      "type": "ExpressionStatement",
      "expression": {
        "type": "CallExpression",
        "callee": {
          "type": "Identifier",
          "name": "alert"
        },
        "arguments": [
          {
            "type": "Literal",
            "value": "hello world!",
          }
        ]
      }
    }
  ],
}

Parcourons d'abord toutes les données avec les noms d'attribut type dans l'AST

  1. ExpressionStatement.
  2. CallExpression
  3. Identifiant
  4. Littéral

Il existe 4 types au total, nous analyserons ensuite ces 4 types de nœuds séparément, en commençant par le plus simple

Literal

{
    "type": "Literal",
    "value": "hello world!",
}

Pour le contenu littéral, tout ce dont nous avons besoin est un attribut de valeur, qui peut être renvoyé directement.

if(node.type === 'Literal') {
    return node.value;
}

N'est-ce pas très simple ?

Identifiant

{
    "type": "Identifier",
    "name": "alert"
},

L'identifiant est également très simple. Il représente une variable que nous avons déjà. Le nom de la variable est node.name. Puisqu'il s'agit d'une variable existante, sa valeur est Quoi ?

if(node.type === 'Identifier') {
    return {
      name: node.name,
      value:this[node.name]
    };
}

Le alert ci-dessus que nous obtenons de node.name est un personnage, via this['xxxxx'] nous pouvons accéder à l'identifiant (Identifier) ​​​​

ExpressionStatement

{
    "type": "ExpressionStatement",
    "expression": {...}
}

C'est en fait très simple, il n'y a pas de contenu substantiel, le vrai contenu est dans l'attribut

, vous pouvez donc renvoyer directement le contenu de l'expression expression

if(node.type === 'ExpressionStatement') {
    return parseAstNode(node.expression);
}
CallExpression

CallExpression signifie littéralement une expression d'appel de fonction, ce qui est un peu plus gênant

{
    "type": "CallExpression",
    "callee": {...},
    "arguments": [...]
}
CallExpression a 2 champs dont nous avons besoin :

    appelé est une référence à la fonction, et le contenu à l'intérieur est un identifiant, qui peut être traité en utilisant la méthode ci-dessus. Le contenu à l'intérieur des
  1. arguments est le tableau de paramètres passé lors de l'appel. Ce que nous devons actuellement traiter est un littéral, le même que ci-dessus. Il existe déjà des moyens d'y faire face.
Cela dit, je pense que vous savez déjà comment le faire

if(node.type === 'CallExpression') {

    // 函数
    const callee = 调用 Identifier 处理器

    // 参数
    const args = node.arguments.map(arg => {
      return 调用 Literal 处理器
    });

    callee(...args);
}
Code

Voici une implémentation simple qui peut être exécuté Le processus ci-dessus ne peut être exécuté que et d'autres fonctionnalités n'ont pas encore été implémentées.

https://github.com/noahlam/pr...

Autres méthodes d'implémentation

En plus de la méthode la plus lourde que j'ai présentée ci-dessus, en fait, js Il existe plusieurs façons d'exécuter directement du code de chaîne

    Insérer un script DOM
  1.   const script = document.createElement("script");
      script.innerText = 'alert("hello world!")';
      document.body.appendChild(script);
    eval
  1. eval('alert("hello world!")')
    nouvelle fonction
  1. new Function('alert("hello world")')();
    famille setTimeout
  1. setTimeout('console.log("hello world")');
Cependant, ceux-ci sont impitoyablement bloqués dans le mini programme...

Enfin, donnez Nous recommandons un
groupe de communication interne avancé d'apprentissage front-end 685910553 (partage d'informations front-end), peu importe où vous êtes sur terre, peu importe depuis combien d'années vous travaillez, vous êtes invités à nous rejoindre ! (Le groupe fournira régulièrement des livres d'étude gratuits et du matériel collecté par le propriétaire du groupe ainsi que des questions d'entretien et des documents de réponses compilés !)

Si vous avez des objections à cet article, veuillez écrire dans la section commentaires du article vos commentaires.

Si vous trouvez cet article intéressant, partagez-le et transmettez-le, ou vous pouvez également le suivre pour montrer votre reconnaissance et vos encouragements à l'égard de notre article.

J'espère que chacun pourra aller de plus en plus loin sur le chemin de la programmation.

Tutoriel recommandé : "Tutoriel JS"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer