AST交互-非连续判断三元表达式转if语句

阅读时间:全文 995 字,预估用时 5 分钟
创作日期:2022-08-21
文章标签:
 
BEGIN

通常使用三元表达式转switch语句的代码可以完成大部分的三元表达式代码美化需求,但是如果三元表达式的判断条件不是连续数字时,此时更适合转为if/else语句,如代码:

function _$du() {
  var _$$I;
  _$$I = _$gf[++_$f1];
  _$$I <= 37
    ? _$$I <= 24
      ? (_$fx = _$jA[--_$g3], _$cR = _$jA[--_$g3])
      : _$$I <= 27
        ? (_$kC = _$gf[++_$f1], _$fx = _$gf[++_$f1], _$cR = _$dz[_$kC])
        : _$$I <= 33
          ? (_$fx = _$gf[++_$f1], _$cR = _$gD)
          : (_$gf[_$f1] = 92, _$fx = _$f3[_$gf[++_$f1]], _$gf[_$f1] = _$fx, _$cR = _$jA[--_$g3])
    : _$$I <= 92
      ? _$$I <= 54
        ? (_$fx = _$gf[++_$f1], _$cR = _$cL)
        : _$$I <= 62
          ? (_$fx = _$gf[++_$f1], _$cR = _$fs)
          : _$$I <= 63
            ? (_$fx = _$gf[++_$f1], _$cR = _$aG)
            : (_$fx = _$gf[++_$f1], _$cR = _$jA[--_$g3])
      : _$$I <= 99
        ? (_$gf[_$f1] = 92, _$fx = _$j4[_$gf[++_$f1]], _$gf[_$f1] = _$fx, _$cR = _$jA[--_$g3])
        : _$$I <= 104
          ? (_$kC = _$gf[++_$f1], _$fx = _$gf[++_$f1], _$cR = _$ko[_$kC])
          : (--_$g3, _$cR = _$jA, _$fx = _$g3)
}

碰到非连续判断条件的三元表达式,我们需要转换后的格式是这样的:

function _$du() {
  var _$$I;
  _$$I = _$gf[++_$f1];
  if (_$$I <= 24) {
    _$fx = _$jA[--_$g3];
    _$cR = _$jA[--_$g3];
  } else if (_$$I > 24 && _$$I <= 27) {
    _$kC = _$gf[++_$f1];
    _$fx = _$gf[++_$f1];
    _$cR = _$dz[_$kC];
  } else if (_$$I > 27 && _$$I <= 33) {
    _$fx = _$gf[++_$f1];
    _$cR = _$gD;
  } else if (_$$I > 33 && _$$I <= 37) {
    _$gf[_$f1] = 92;
    _$fx = _$f3[_$gf[++_$f1]];
    _$gf[_$f1] = _$fx;
    _$cR = _$jA[--_$g3];
  } else if (_$$I > 37 && _$$I <= 54) {
    _$fx = _$gf[++_$f1];
    _$cR = _$cL;
  } else if (_$$I > 54 && _$$I <= 62) {
    _$fx = _$gf[++_$f1];
    _$cR = _$fs;
  } else if (_$$I > 62 && _$$I <= 63) {
    _$fx = _$gf[++_$f1];
    _$cR = _$aG;
  } else if (_$$I > 63 && _$$I <= 92) {
    _$fx = _$gf[++_$f1];
    _$cR = _$jA[--_$g3];
  } else if (_$$I > 92 && _$$I <= 99) {
    _$gf[_$f1] = 92;
    _$fx = _$j4[_$gf[++_$f1]];
    _$gf[_$f1] = _$fx;
    _$cR = _$jA[--_$g3];
  } else if (_$$I > 99 && _$$I <= 104) {
    _$kC = _$gf[++_$f1];
    _$fx = _$gf[++_$f1];
    _$cR = _$ko[_$kC];
  } else {
    --_$g3;
    _$cR = _$jA;
    _$fx = _$g3;
  }
}

修改前面三元表达式转switch语句getIfNode的代码:

function getIfNode(node, ans={}, opt = {}, historys = [], keytype=null) {
  const testValue = getIfTestValue(node?.test)
  if (testValue === false) return
  if (!opt.name) { Object.assign(opt, testValue) }
  if (testValue.name !== opt.name) return
  if (historys.length > 0 && keytype) {
    historys[historys.length - 1][keytype] = false
  }
  historys.push({ ...testValue, consequent: true, alternate: true })
  ;['consequent', 'alternate'].forEach(key => {
    const nextNode = getNode(node[key])
    const idx = key === 'consequent' ? testValue.value : getPreValue(historys, testValue.value)
    ans[idx] = nextNode
    // console.log(key, idx)
    nextNode?.forEach(item => getIfNode(item, ans, opt, historys, key))
  })
  return [opt.name, ans]
}

可以看到将存数据的数组改成的对象,并且新增了historys数组和keytype变量来辅助编码,historys数据用于记录历史节点的值,并使用consequent和alternate判断是否赋值,值为true表示完成,false表示步完成,然后在三元表达式的否语句节点上通过调用getPreValue(historys, testValue.value)方法返回当前节点需要存储的下标,getPreValue方法定义如下:

function getPreValue(historys, val) {
  let len = historys.length
  while(len--) {
    const his = historys[len]
    if (!his.consequent && his.value > val) return his.value
  }
  return 'else'
}

最后将判断的键名与代码匹配对象返回出来,代码匹配对象就是三元表达式的判断值与对应执行代码的匹配关系。格式如:

{
    24: [...],
    27: [...],
    ...
    92: [...],
    ...
    104: [...],
    else: [...]
}

拿到代码匹配对象后接下来需要生成ast并替换掉,其中traverse代码如下:

const visitor = {
  ConditionalExpression(path) {
    const [key, data] = getIfNode(path.node) || []
    const newNode = grenIfAst(0, key, Object.keys(data), data)
    path.replaceWithMultiple([newNode])
    path.skip()
  },
}
traverse(ast, visitor)

可以看到key为键名,data为代码匹配对象,最后通过grenIfAst方法生成AST树再通过replaceWithMultiple方法替换掉原来的节点,grenIfAst方法代码如下:

function grenIfAst(current, key, idxs, data) {
  if (current >= idxs.length) return undefined
  const statements = data[idxs[current]]?.reduce((ans, item) =>
    ([...ans, (t.isStatement(item) ? item : t.expressionStatement(item))])
  , []) || []
  if (idxs[current] === 'else') return t.blockStatement(statements)
  const node = grenIfAst(current + 1, key, idxs, data)
  if (current === 0) {
    return t.ifStatement(
      t.binaryExpression('<=', t.identifier(key), t.numericLiteral(Number(idxs[current]))),
      t.blockStatement(statements),
      node
    )
  } else {
    return t.ifStatement(
      t.logicalExpression(
        '&&',
        t.binaryExpression('>', t.identifier(key), t.numericLiteral(Number(idxs[current - 1]))),
        t.binaryExpression('<=', t.identifier(key), t.numericLiteral(Number(idxs[current])))
      ),
      t.blockStatement(statements),
      node
    )
  }
}

这样三元表达式转if/else表达式的代码美化就完成了,作者在开发当中发现替换节点时使用replaceWith方法就不行,使用replaceWithMultiple方法才可以,暂不知道啥原因,有空再看。

FINISH

随机文章
人生倒计时
default