用js 实现 一个lisp 解释器的第二步:实现EVAL

404 阅读3分钟

本文简介

第一步简单实现了一个REPL的控制结构,但是对输入的数据并没有做处理,而是直接原封不动的打印了出来,针对数据处理(求值)的这部分,则在本文中实现

如: (+ 1 2)--> 3, (+ (* 2 3)) --> 6

本文目标

完成对基本元素的数学运算(加减乘除)

实现线路图

step2_eval.png

1 需要关系型数据(上文中的环境)来保存我们的加减乘除的操作

// 定义一个简单的REPL 环境, 这个环境是一个关联数据结构。将符号(symbols names)映射为数学运算函数
// [ Symbol { value: '+' }, 1, [ Symbol { value: '*' }, 2, 3 ] ] --> [ [Function], 2, 3 ], [ [Function], 1, 6 ] 
var repl_env = {}
var rep = function(str){ return PRINT(EVAL(READ(str), repl_env))}

repl_env['+'] = function (a, b){return a + b}
repl_env['-'] = function (a, b){return a - b}
repl_env['*'] = function (a, b){return a * b}
repl_env['/'] = function (a, b){return a / b}

2. 修改EVAL函数,此时接受两个参数,一个为输入的字符,和定义的环境

// 值考虑(),基本数字,和基本运算符,不包含向量,哈希表等。。
// 求值器 EVAL-APPLY 循环
function EVAL(ast, env) {
  var result = _EVAL(ast, env)
  return (typeof result !== 'undefined') ? result : null
}

function _EVAL(ast, env) {
  // ast 不是一个列表
  if (!types._list_Q(ast)) {
    return eval_ast(ast, env)
  }
  // 是一个空的列表
  if (ast.length === 0) {
    return ast
  }
  // 不为空的列表
  var el = eval_ast(ast, env)
  var f = el[0]
  return f.apply(el, el.slice(1))
}

3. 定义 eval_list,用于对ast的数据类型做判断处理

function eval_ast(ast, env) {
  // 是操作符号
  console.log(ast, 'ast');
  if (types._symbol_Q(ast)) {
    if (ast.value in env) {
      return env[ast.value];
    } else {
      throw new Error("'" + ast.value + "' not found");
    }
  } else if (types._list_Q(ast)) { // 是一个列表如 [ Symbol: {value : " + "}, 1,2]
    return ast.map(function(a) {return EVAL(a, env)}) 
  } else { // 基本元素 1, 2 。。。
    return ast
  }
}

4. 求值器的内核: EVAL-APPLY 之间的相互作用

EVAL 为 APPLY 提供 过程和实际参数,APPLY 为EVAL 提供 环境和表达式

image.png

  1. step2_eval.js 代码

if (typeof module !== 'undefined') {
  var types = require('./types')
  var readline = require('./node_readline')
  var printer = require('./printer')
  var reader = require('./reader')
}

function READ(str) {
  return reader.read_str(str)
}
// 求值器 EVAL-APPLY 循环
function EVAL(ast, env) {
  var result = _EVAL(ast, env)
  return (typeof result !== 'undefined') ? result : null
}
// 值考虑(),基本数字,和基本运算符,不包含向量,哈希表等。。
function _EVAL(ast, env) {
  // ast 不是一个列表
  if (!types._list_Q(ast)) {
    return eval_ast(ast, env)
  }
  // 是一个空的列表
  if (ast.length === 0) {
    return ast
  }
  // 不为空的列表
  var el = eval_ast(ast, env)
  console.log(el, 'el')
  var f = el[0]
  return f.apply(el, el.slice(1))
}
// 对ast 的类型进行匹配,并做出相应处理
function eval_ast(ast, env) {
  // 是操作符号
  console.log(ast, 'ast');
  if (types._symbol_Q(ast)) {
    if (ast.value in env) {
      return env[ast.value];
    } else {
      throw new Error("'" + ast.value + "' not found");
    }
  } else if (types._list_Q(ast)) { // 是一个列表如 [ Symbol: {value : " + "}, 1,2]
    return ast.map(function(a) {return EVAL(a, env)}) 
  } else { // 基本元素 1, 2 。。。
    return ast
  }
}

function PRINT(exp) {
  return printer._pr_str(exp, true)
}
// 定义一个简单的REPL 环境, 这个环境是一个关联数据结构。将符号(symbols names)映射为数学运算函数
// [ Symbol { value: '+' }, 1, [ Symbol { value: '*' }, 2, 3 ] ] --> [ [Function], 2, 3 ], [ [Function], 1, 6 ] 
var repl_env = {}
var rep = function(str){ return PRINT(EVAL(READ(str), repl_env))}

repl_env['+'] = function (a, b){return a + b}
repl_env['-'] = function (a, b){return a - b}
repl_env['*'] = function (a, b){return a * b}
repl_env['/'] = function (a, b){return a / b}


// repl loop 循环打印
// 检查当前环境是否为node
if (typeof require !== 'undefined' && require.main === module) {
  while (true) {
    var line = readline.readline("user>")
    if (line === null) { break}
    try {
      if (line) {printer.println(rep(line))}
    } catch (exc) {
      if (exc instanceof reader.BlankException) {continue}
      if (exc instanceof Error) {console.warn(exc.stack)} else {
        console.warn('Error: ' + printer._pr_str(exc, true))}
    }
  }
}