返回

语法分析实战二:语法分析之 纯手工实现一个公式计算器JS版

见解分享

标注




正文

语法分析实战二:语法分析之 纯手工实现一个公式计算器JS版

在计算机科学领域,语法分析是编译器和解释器中至关重要的一个阶段。它负责将源代码中的字符序列解析成计算机能够理解的抽象语法树(AST),为之后的语义分析和代码生成奠定基础。在这一实战中,我们将从零开始,纯手工实现一个公式计算器,亲身体验语法分析的过程。

递归下降:一种自顶向下的解析方法

在语法分析中,递归下降是一种自顶向下的解析方法。它按照语法规则从上至下逐层解析输入,逐步建立语法树。

例如,对于一个加法表达式 a + b,递归下降的解析过程如下:

  1. 首先,识别到这是一个加法表达式,因为它符合加法表达式的语法规则。
  2. 接着,递归地解析加法表达式的左右两边。
  3. 重复步骤 2,直到解析到不可再分的终结符(如数字或变量)。
  4. 将解析结果组合起来,构建成抽象语法树。

上下文无关文法:语法规则的数学模型

上下文无关文法(CFG)是语法规则的数学模型。它由产生式和终结符/非终结符组成。

产生式定义了如何从一个非终结符派生出新的符号序列。例如,对于加法表达式,产生式可以是:

E -> E + T
E -> T
T -> T * F
T -> F
F -> (E)
F -> num

其中,ETF 是非终结符,num 是终结符。

JavaScript 实现

基于上述理论,我们可以在 JavaScript 中实现一个公式计算器。具体步骤如下:

  1. 定义终结符和非终结符 :终结符包括数字、加号、乘号、括号等;非终结符包括表达式(E)、项(T)、因子(F)。
  2. 编写递归下降函数 :根据 CFG 定义的产生式,编写递归下降函数来解析输入。
  3. 构建抽象语法树 :解析过程中,将解析结果组合起来,构建抽象语法树。
  4. 计算结果 :根据抽象语法树,计算表达式的值。
// 定义终结符和非终结符
const TERMINALS = ["+", "*", "(", ")", "num"];
const NON_TERMINALS = ["E", "T", "F"];

// 编写递归下降函数
const parse = (input) => {
  let pos = 0; // 当前位置
  let token = input[pos]; // 当前令牌

  // 解析表达式
  const E = () => {
    let left = T();
    while (token === "+") {
      pos++;
      left += T();
    }
    return left;
  };

  // 解析项
  const T = () => {
    let left = F();
    while (token === "*") {
      pos++;
      left *= F();
    }
    return left;
  };

  // 解析因子
  const F = () => {
    if (token === "(") {
      pos++;
      const exp = E();
      pos++; // 匹配右括号
      return exp;
    } else if (token === "num") {
      const num = parseFloat(token);
      pos++;
      return num;
    } else {
      throw new Error("Invalid input");
    }
  };

  // 开始解析
  const result = E();

  // 检查是否还有未解析的字符
  if (pos < input.length) {
    throw new Error("Invalid input");
  }

  return result;
};

// 示例输入
const input = "2 + 3 * 4";

// 解析并计算结果
const result = parse(input);
console.log(`计算结果:${result}`);

总结

通过这个实战,我们深入理解了语法分析中的递归下降法和上下文无关文法。我们还亲手实现了一个公式计算器,体验了从源代码到抽象语法树再到计算结果的整个过程。这些知识和技能对于理解编译原理、设计计算机语言和构建高级语言程序至关重要。