azl397985856 / fe-interview

宇宙最强的前端面试指南 (https://lucifer.ren/fe-interview)
Apache License 2.0
2.83k stars 260 forks source link

【每日一题】- 2020-05-16 - 解析字符串 #128

Closed azl397985856 closed 4 years ago

azl397985856 commented 4 years ago

给你一个字符串if (x > 5) { console.log('greater than 5')} else { console.log('less than 5')}

预期:

let a = 1;
const sourceCode = "if (x > 5) { console.log('greater than 5')} else { console.log('less than 5')}";
function eval(text) {
   // do something
}

eval(souceCode) // 'less than 5'
a = 6
eval(souceCode) // 'great than 5'

要求:

feikerwu commented 4 years ago

一个比较挫的想法,放入到script执行🤓🤓🤓

function eval(sourceCode) {
  const reg = /(?<=[\[\(\{\s])x(?=[\[\(\{\s])/
  sourceCode = sourceCode.replace(reg, 'a')
  const script = document.createElement('script')
  script.type = 'text/javascript'
  script.innerHTML = sourceCode
  document.body.appendChild(script)
}
azl397985856 commented 4 years ago

@feikerwu 😂 不准用script 标签, 我改下题目描述

feikerwu commented 4 years ago

说下想法,在不用Function,eval, script 的前提下,我们要去执行给定的一段字符,问题转化下其实就是

如何在不用原生提供的js解释器情况下去执行一段js脚本

那很朴素的想法就是,提供方不给执行,我们自己造一个解释器去跑不就好了,至于最后能不能造出来我们另说。所以问题再转化下就是

如何造一个解释器去执行上述提供的字符脚本

解释器就是一段程序,输入是sourcecode字符,程序功能是识别sourcecode内容,并做相应的处理,sourcecode由条件,循环,变量定义,运算等组成,但不管怎么样,这些功能都是可穷举的。 所以第一步就是将sourcecode翻译成上述的功能聚合,以计算机可识别的形式表示,编译原理天然的采用树的结果,或者说AST(Abstract Syntax Tree,抽象语法树),下面是用 acorn 将上述sourcecode转化出来的语法树表示

{
  "type": "Program",
  "start": 0,
  "end": 78,
  "body": [
    {
      "type": "IfStatement",
      "start": 0,
      "end": 78,
      "test": {
        "type": "BinaryExpression",
        "start": 4,
        "end": 9,
        "left": {
          "type": "Identifier",
          "start": 4,
          "end": 5,
          "name": "x"
        },
        "operator": ">",
        "right": {
          "type": "Literal",
          "start": 8,
          "end": 9,
          "value": 5,
          "raw": "5"
        }
      },
      "consequent": {
        "type": "BlockStatement",
        "start": 11,
        "end": 43,
        "body": [
          {
            "type": "ExpressionStatement",
            "start": 13,
            "end": 42,
            "expression": {
              "type": "CallExpression",
              "start": 13,
              "end": 42,
              "callee": {
                "type": "MemberExpression",
                "start": 13,
                "end": 24,
                "object": {
                  "type": "Identifier",
                  "start": 13,
                  "end": 20,
                  "name": "console"
                },
                "property": {
                  "type": "Identifier",
                  "start": 21,
                  "end": 24,
                  "name": "log"
                },
                "computed": false
              },
              "arguments": [
                {
                  "type": "Literal",
                  "start": 25,
                  "end": 41,
                  "value": "greater than 5",
                  "raw": "'greater than 5'"
                }
              ]
            }
          }
        ]
      },
      "alternate": {
        "type": "BlockStatement",
        "start": 49,
        "end": 78,
        "body": [
          {
            "type": "ExpressionStatement",
            "start": 51,
            "end": 77,
            "expression": {
              "type": "CallExpression",
              "start": 51,
              "end": 77,
              "callee": {
                "type": "MemberExpression",
                "start": 51,
                "end": 62,
                "object": {
                  "type": "Identifier",
                  "start": 51,
                  "end": 58,
                  "name": "console"
                },
                "property": {
                  "type": "Identifier",
                  "start": 59,
                  "end": 62,
                  "name": "log"
                },
                "computed": false
              },
              "arguments": [
                {
                  "type": "Literal",
                  "start": 63,
                  "end": 76,
                  "value": "less than 5",
                  "raw": "'less than 5'"
                }
              ]
            }
          }
        ]
      }
    }
  ],
  "sourceType": "module"
}

可以看到每个节点都有一个 type, 就是上面说的可穷举的程序节点,比如IfStatement(条件)Literal等等。

第二步,我们拿到上述AST之后,可以给每个type节点加一个处理函数,比如看到ifStatement怎么做,看到 console 的 Identifier 怎么做等等。

定义好每个节点的处理函数之后,从AST root遍历这棵树,对每个节点执行相应的函数

azl397985856 commented 4 years ago

@feikerwu 非常棒。 就是这个意思

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.