溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么編寫一個(gè)javascript元循環(huán)求值器

發(fā)布時(shí)間:2020-08-04 10:00:49 來(lái)源:億速云 閱讀:153 作者:小豬 欄目:web開發(fā)

小編這次要給大家分享的是怎么編寫一個(gè)javascript元循環(huán)求值器,文章內(nèi)容豐富,感興趣的小伙伴可以來(lái)了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。

結(jié)構(gòu)

一個(gè)元循環(huán)求值器,完整的應(yīng)該包含以下內(nèi)容:

  • tokenizer:對(duì)代碼文本進(jìn)行詞法和語(yǔ)法分析,將代碼分割成若干個(gè)token
  • parser:根據(jù)token,生成AST樹
  • evaluate:根據(jù)AST樹節(jié)點(diǎn)的type,執(zhí)行對(duì)應(yīng)的apply方法
  • apply:根據(jù)環(huán)境,執(zhí)行實(shí)際的求值計(jì)算
  • scope:當(dāng)前代碼執(zhí)行的環(huán)境

代碼目錄

根據(jù)結(jié)構(gòu)看,我將代碼目錄大致拆分為以下幾個(gè)文件

  • parser
  • eval
  • scope

tokenizer和parser這兩個(gè)過(guò)程不是本文的重點(diǎn),我統(tǒng)一放在了parser中,交由 @babel/parser 來(lái)處理。
evaluate和apply這兩個(gè)過(guò)程我統(tǒng)一放在了eval文件中處理,一會(huì)我們重點(diǎn)看下這部分。
scope則放入scope文件。

evaluate-apply

這其實(shí)是一個(gè)遞歸計(jì)算的過(guò)程。

首先,evaluate 接收兩個(gè)參數(shù),node 當(dāng)前遍歷的AST樹節(jié)點(diǎn)和 scope 當(dāng)前環(huán)境。然后,evaluate去根據(jù) node 的 type 屬性,判斷該節(jié)點(diǎn)是什么類型。判斷出類型后,執(zhí)行 apply 去求值這個(gè)節(jié)點(diǎn)所代表的表達(dá)式。apply 中會(huì)再次遞歸的執(zhí)行 evaluate 去計(jì)算當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。最終,執(zhí)行完整顆AST樹。

我們來(lái)看下具體代碼吧

const evaluate = (node: t.Node, scope) => {
 const evalFunc = evaluateMap[node.type];
 if (!evalFunc) {
 throw `${node.loc} ${node.type} 還未實(shí)現(xiàn)`;
 }
 return evalFunc(node, scope);
}

以上就是evaluate具體做的事。

其中,evaluateMap 是目前實(shí)現(xiàn)的內(nèi)容集合,我們來(lái)看下具體的代碼

const evaluateMap: EvaluateMap = {
 File(node: t.File, scope) {
 evaluate(node.program, scope);
 },

 Program(node: t.Program, scope) {
 for (const n of node.body) {
  evaluate(n, scope);
 }
 },

 Identifier(node: t.Identifier, scope) {
 const $var = scope.$find(node.name);
 if (!$var) {
  throw `[Error] ${node.loc}, '${node.name}' 未定義`;
 }
 return $var.$get();
 },

 StringLiteral(node: t.StringLiteral, scope) {
 return node.value;
 },

 NumericLiteral(node: t.NumericLiteral, scope) {
 return node.value;
 },

 BooleanLiteral(node: t.BooleanLiteral, scope) {
 return node.value;
 },

 NullLiteral(node: t.NullLiteral, scope) {
 return null;
 },

 BlockStatement(block: t.BlockStatement, scope) {
 const blockScope = scope.shared ? scope : new Scope('block', scope);
 for (const node of block.body) {
  const res = evaluate(node, blockScope);
  if (res === BREAK || res === CONTINUE || res === RETURN) {
  return res;
  }
 }
 },

 DebuggerStatement(node: t.DebuggerStatement, scope) {
 debugger;
 },

 ExpressionStatement(node: t.ExpressionStatement, scope) {
 evaluate(node.expression, scope);
 },

 ReturnStatement(node: t.ReturnStatement, scope) {
 RETURN.result = (node.argument ? evaluate(node.argument, scope) : void 0);
 return RETURN;
 },

 BreakStatement(node: t.BreakStatement, scope) {
 return BREAK;
 },

 ContinueStatement(node: t.ContinueStatement, scope) {
 return CONTINUE;
 },

 IfStatement(node: t.IfStatement, scope) {
 if (evaluate(node.test, scope)) {
  return evaluate(node.consequent, scope);
 }

 if (node.alternate) {
  const ifScope = new Scope('block', scope, true);
  return evaluate(node.alternate, ifScope)
 }
 },

 SwitchStatement(node: t.SwitchStatement, scope) {
 const discriminant = evaluate(node.discriminant, scope);
 const switchScope = new Scope('switch', scope);
 for (const ca of node.cases){
  if (ca.test === null || evaluate(ca.test, switchScope) === discriminant) {
  const res = evaluate(ca, switchScope);
  if (res === BREAK) {
   break;
  } else if (res === RETURN) {
   return res;
  }
  }
 }
 },

 SwitchCase(node: t.SwitchCase, scope) {
 for (const item of node.consequent) {
  const res = evaluate(item, scope);
  if (res === BREAK || res === RETURN) {
  return res;
  }
 }
 },

 ThrowStatement(node: t.ThrowStatement, scope) {
 throw evaluate(node.argument, scope);
 },

 TryStatement(node: t.TryStatement, scope) {
 try {
  return evaluate(node.block, scope);
 } catch (error) {
  if (node.handler) {
  const catchScope = new Scope('block', scope, true);
  catchScope.$let((<t.Identifier>node.handler.param).name, error);
  return evaluate(node.handler, catchScope);
  } else {
  throw error;
  }
 } finally {
  if (node.finalizer) {
  return evaluate(node.finalizer, scope);
  }
 }
 },

 CatchClause(node: t.CatchClause, scope) {
 return evaluate(node.body, scope);
 },

 WhileStatement(node: t.WhileStatement, scope) {
 while (evaluate(node.test, scope)) {
  const whileScope = new Scope('loop', scope, true);
  const res = evaluate(node.body, whileScope);
  if (res === CONTINUE) continue;
  if (res === BREAK) break;
  if (res === RETURN) return res;
 }
 },

 ForStatement(node: t.ForStatement, scope) {
 for (
  const forScope = new Scope('loop', scope),
  initVal = evaluate(node.init, forScope);
  evaluate(node.test, forScope);
  evaluate(node.update, forScope)
 ) {
  const res = evaluate(node.body, forScope);
  if (res === CONTINUE) continue;
  if (res === BREAK) break;
  if (res === RETURN) return res;
 }
 },

 ForInStatement(node: t.ForInStatement, scope) {
 const kind = (<t.VariableDeclaration>node.left).kind;
 const decl = (<t.VariableDeclaration>node.left).declarations[0];
 const name = (<t.Identifier>decl.id).name;

 for (const value in evaluate(node.right, scope)) {
  const forScope = new Scope('loop', scope, true);
  scope.$define(kind, name, value);
  const res = evaluate(node.body, forScope);
  if (res === CONTINUE) continue;
  if (res === BREAK) break;
  if (res === RETURN) return res;
 }
 },

 ForOfStatement(node: t.ForOfStatement, scope) {
 const kind = (<t.VariableDeclaration>node.left).kind;
 const decl = (<t.VariableDeclaration>node.left).declarations[0];
 const name = (<t.Identifier>decl.id).name;

 for (const value of evaluate(node.right, scope)) {
  const forScope = new Scope('loop', scope, true);
  scope.$define(kind, name, value);
  const res = evaluate(node.body, forScope);
  if (res === CONTINUE) continue;
  if (res === BREAK) break;
  if (res === RETURN) return res;
 }
 },

 FunctionDeclaration(node: t.FunctionDeclaration, scope) {
 const func = evaluateMap.FunctionExpression(node, scope);
 scope.$var(node.id.name, func);
 },

 VariableDeclaration(node: t.VariableDeclaration, scope) {
 const { kind, declarations } = node;
 for (const decl of declarations) {
  const varName = (<t.Identifier>decl.id).name;
  const value = decl.init &#63; evaluate(decl.init, scope) : void 0;
  if (!scope.$define(kind, varName, value)) {
  throw `[Error] ${name} 重復(fù)定義`
  }
 }
 },

 ThisExpression(node: t.ThisExpression, scope) {
 const _this = scope.$find('this');
 return _this &#63; _this.$get() : null;
 },

 ArrayExpression(node: t.ArrayExpression, scope) {
 return node.elements.map(item => evaluate(item, scope));
 },

 ObjectExpression(node: t.ObjectExpression, scope) {
 let res = Object.create(null);
 node.properties.forEach((prop) => {
  let key;
  let value;
  if(prop.type === 'ObjectProperty'){
  key = prop.key.name;
  value = evaluate(prop.value, scope);
  res[key] = value;
  }else if (prop.type === 'ObjectMethod'){
  const kind = prop.kind;
  key = prop.key.name;
  value = evaluate(prop.body, scope);
  if(kind === 'method') {
   res[key] = value;
  }else if(kind === 'get') {
   Object.defineProperty(res, key, { get: value });
  }else if(kind === 'set') {
   Object.defineProperty(res, key, { set: value });
  }
  }else if(prop.type === 'SpreadElement'){
  const arg = evaluate(prop.argument, scope);
  res = Object.assign(res, arg);
  }
 });
 return res;
 },

 FunctionExpression(node: t.FunctionExpression, scope) {
 return function (...args: any) {
  const funcScope = new Scope('function', scope, true);
  node.params.forEach((param: t.Identifier, idx) => {
  const { name: paramName } = param;
  funcScope.$let(paramName, args[idx]);
  });
  funcScope.$const('this', this);
  funcScope.$const('arguments', arguments);
  const res = evaluate(node.body, funcScope);
  if (res === RETURN) {
  return res.result;
  }
 }
 },

 ArrowFunctionExpression(node: t.ArrowFunctionExpression, scope) {
 return (...args) => {
  const funcScope = new Scope('function', scope, true);
  node.params.forEach((param: t.Identifier, idx) => {
  const { name: paramName } = param;
  funcScope.$let(paramName, args[idx]);
  });
  const _this = funcScope.$find('this');
  funcScope.$const('this', _this &#63; _this.$get() : null);
  funcScope.$const('arguments', args);
  const res = evaluate(node.body, funcScope);
  if (res === RETURN) {
  return res.result;
  }
 }
 },

 UnaryExpression(node: t.UnaryExpression, scope) {
 const expressionMap = {
  '~': () => ~evaluate(node.argument, scope),
  '+': () => +evaluate(node.argument, scope),
  '-': () => -evaluate(node.argument, scope),
  '!': () => !evaluate(node.argument, scope),
  'void': () => void evaluate(node.argument, scope),
  'typeof': () => {
  if (node.argument.type === 'Identifier') {
   const $var = scope.$find(node.argument.name);
   const value = $var &#63; $var.$get() : void 0;
   return typeof value;
  }
  return typeof evaluate(node.argument, scope);
  },
  'delete': () => {
  if (node.argument.type === 'MemberExpression') {
   const { object, property, computed } = node.argument;
   const obj = evaluate(object, scope);
   let prop;
   if (computed) {
   prop = evaluate(property, scope);
   } else {
   prop = property.name;
   }
   return delete obj[prop];
  } else {
   throw '[Error] 出現(xiàn)錯(cuò)誤'
  }
  },
 }
 return expressionMap[node.operator]();
 },

 UpdateExpression(node: t.UpdateExpression, scope) {
 const { prefix, argument, operator } = node;
 let $var: IVariable;
 if (argument.type === 'Identifier') {
  $var = scope.$find(argument.name);
  if (!$var) throw `${argument.name} 未定義`;
 } else if (argument.type === 'MemberExpression') {
  const obj = evaluate(argument.object, scope);
  let prop;
  if (argument.computed) {
  prop = evaluate(argument.property, scope);
  } else {
  prop = argument.property.name;
  }
  $var = {
  $set(value: any) {
   obj[prop] = value;
   return true;
  },
  $get() {
   return obj[prop];
  }
  }
 } else {
  throw '[Error] 出現(xiàn)錯(cuò)誤'
 }

 const expressionMap = {
  '++': v => {
  $var.$set(v + 1);
  return prefix &#63; ++v : v++
  },
  '--': v => {
  $var.$set(v - 1);
  return prefix &#63; --v : v--
  },
 }

 return expressionMap[operator]($var.$get());
 },

 BinaryExpression(node: t.BinaryExpression, scope) {
 const { left, operator, right } = node;
 const expressionMap = {
  '==': (a, b) => a == b,
  '===': (a, b) => a === b,
  '>': (a, b) => a > b,
  '<': (a, b) => a < b,
  '!=': (a, b) => a != b,
  '!==': (a, b) => a !== b,
  '>=': (a, b) => a >= b,
  '<=': (a, b) => a <= b,
  '<<': (a, b) => a << b,
  '>>': (a, b) => a >> b,
  '>>>': (a, b) => a >>> b,
  '+': (a, b) => a + b,
  '-': (a, b) => a - b,
  '*': (a, b) => a * b,
  '/': (a, b) => a / b,
  '&': (a, b) => a & b,
  '%': (a, b) => a % b,
  '|': (a, b) => a | b,
  '^': (a, b) => a ^ b,
  'in': (a, b) => a in b,
  'instanceof': (a, b) => a instanceof b,
 }
 return expressionMap[operator](evaluate(left, scope), evaluate(right, scope));
 },

 AssignmentExpression(node: t.AssignmentExpression, scope) {
 const { left, right, operator } = node;
 let $var: IVariable;

 if (left.type === 'Identifier') {
  $var = scope.$find(left.name);
  if(!$var) throw `${left.name} 未定義`;
 } else if (left.type === 'MemberExpression') {
  const obj = evaluate(left.object, scope);
  let prop;
  if (left.computed) {
  prop = evaluate(left.property, scope);
  } else {
  prop = left.property.name;
  }
  $var = {
  $set(value: any) {
   obj[prop] = value;
   return true;
  },
  $get() {
   return obj[prop];
  }
  }
 } else {
  throw '[Error] 出現(xiàn)錯(cuò)誤'
 }

 const expressionMap = {
  '=': v => { $var.$set(v); return $var.$get() },
  '+=': v => { $var.$set($var.$get() + v); return $var.$get() },
  '-=': v => { $var.$set($var.$get() - v); return $var.$get() },
  '*=': v => { $var.$set($var.$get() * v); return $var.$get() },
  '/=': v => { $var.$set($var.$get() / v); return $var.$get() },
  '%=': v => { $var.$set($var.$get() % v); return $var.$get() },
  '<<=': v => { $var.$set($var.$get() << v); return $var.$get() },
  '>>=': v => { $var.$set($var.$get() >> v); return $var.$get() },
  '>>>=': v => { $var.$set($var.$get() >>> v); return $var.$get() },
  '|=': v => { $var.$set($var.$get() | v); return $var.$get() },
  '&=': v => { $var.$set($var.$get() & v); return $var.$get() },
  '^=': v => { $var.$set($var.$get() ^ v); return $var.$get() },
 }

 return expressionMap[operator](evaluate(right, scope));
 },

 LogicalExpression(node: t.LogicalExpression, scope) {
 const { left, right, operator } = node;
 const expressionMap = {
  '&&': () => evaluate(left, scope) && evaluate(right, scope),
  '||': () => evaluate(left, scope) || evaluate(right, scope),
 }
 return expressionMap[operator]();
 },

 MemberExpression(node: t.MemberExpression, scope) {
 const { object, property, computed } = node;
 const obj = evaluate(object, scope);
 let prop;
 if (computed) {
  prop = evaluate(property, scope);
 } else {
  prop = property.name;
 }
 return obj[prop];
 },

 ConditionalExpression(node: t.ConditionalExpression, scope) {
 const { test, consequent, alternate } = node;
 return evaluate(test, scope) &#63; evaluate(consequent, scope) : evaluate(alternate, scope);
 },

 CallExpression(node: t.CallExpression, scope) {
 const func = evaluate(node.callee, scope);
 const args = node.arguments.map(arg => evaluate(arg, scope));
 let _this;
 if (node.callee.type === 'MemberExpression') {
  _this = evaluate(node.callee.object, scope);
 } else {
  const $var = scope.$find('this');
  _this = $var &#63; $var.$get() : null;
 }
 return func.apply(_this, args);
 },

 NewExpression(node: t.NewExpression, scope) {
 const func = evaluate(node.callee, scope);
 const args = node.arguments.map(arg => evaluate(arg, scope));
 return new (func.bind(func, ...args));
 },

 SequenceExpression(node: t.SequenceExpression, scope) {
 let last;
 node.expressions.forEach(expr => {
  last = evaluate(expr, scope);
 })
 return last;
 },
}

以上,evaluate-apply 這個(gè)過(guò)程就完了。

scope

我們?cè)賮?lái)看下 scope 該如何實(shí)現(xiàn)。

class Scope implements IScope {
 public readonly variables: EmptyObj = Object.create(null);

 constructor(
 private readonly scopeType: ScopeType,
 private parent: Scope = null,
 public readonly shared = false,
 ) { }
}

我們構(gòu)造一個(gè)類來(lái)模擬 scope??梢钥吹?,Scope 類包含了以下4個(gè)屬性:

  • variables:當(dāng)前環(huán)境下存在的變量
  • scopeType:當(dāng)前環(huán)境的type
  • parent:當(dāng)前環(huán)境的父環(huán)境
  • shared:有些時(shí)候不需要重復(fù)構(gòu)造子環(huán)境,故用此標(biāo)識(shí)

接下來(lái)我們看下該如何在環(huán)境中聲明變量

首先構(gòu)造一個(gè)類來(lái)模擬變量

class Variable implements IVariable {
 constructor(
 private kind: Kind,
 private value: any
 ){ }

 $get() {
 return this.value
 }

 $set(value: any) {
 if (this.kind === 'const') {
  return false
 }
 this.value = value;
 return true;
 }
}

這個(gè)類中有兩個(gè)屬性和兩個(gè)方法

  • kind 用于標(biāo)識(shí)該變量是通過(guò) var、let 還是 const 聲明
  • value 表示該變量的值
  • $get 和 $set 分別用于獲取和設(shè)置該變量的值

有了 Variable 類之后,我們就可以編寫 Scope 類中的聲明變量的方法了。

let 和 const 的聲明方式基本一樣

$const(varName: string, value: any) {
 const variable = this.variables[varName];
 if (!variable) {
 this.variables[varName] = new Variable('const', value);
 return true;
 }
 return false;
}

$let(varName: string, value: any) {
 const variable = this.variables[varName];
 if (!variable) {
 this.variables[varName] = new Variable('let', value);
 return true;
 }
 return false;
}

var 的聲明方式稍微有一點(diǎn)差異,因?yàn)閖s中,除了在 function 中,用var 聲明的變量是會(huì)被聲明到父級(jí)作用域的(js的歷史遺留坑)。我們看下代碼

$var(varName: string, value: any) {
 let scope: Scope = this;
 while (!!scope.parent && scope.scopeType !== 'function') {
 scope = scope.parent;
 }
 const variable = scope.variables[varName];
 if (!variable) {
 scope.variables[varName] = new Variable('var', value);
 } else {
 scope.variables[varName] = variable.$set(value);
 }
 return true
}

除了聲明,我們還需要一個(gè)尋找變量的方法,該方法會(huì)從當(dāng)前環(huán)境開始,一直沿著作用域鏈,找到最外層的環(huán)境為止。因此,代碼實(shí)現(xiàn)如下

$find(varName: string): null | IVariable {
 if (Reflect.has(this.variables, varName)) {
 return Reflect.get(this.variables, varName);
 }
 if (this.parent) {
 return this.parent.$find(varName);
 }
 return null;
}

以上,一個(gè)基本的javascript元循環(huán)求值器就完成了

看完這篇關(guān)于怎么編寫一個(gè)javascript元循環(huán)求值器的文章,如果覺得文章內(nèi)容寫得不錯(cuò)的話,可以把它分享出去給更多人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI