将Java标记化正则表达式转换为Javascript

时间:2014-03-01 18:18:51

标签: javascript regex infix-notation

作为我的问题Tokenizing an infix string in Java的答案,我得到了正则表达式(?<=[^\.a-zA-Z\d])|(?=[^\.a-zA-Z\d]。但是,现在我在Javascript中编写相同的代码,而且我不知道如何让Javascript正则表达式做同样的事情。

例如,如果我有字符串sin(4+3)*2,我需要将其解析为["sin","(","4","+","3",")","*","2"]

我将使用什么正则表达式将字符串标记为每个单独的部分。

之前,我做的是我只是对每个可能的令牌进行了字符串替换,并在其周围放置一个空格,然后拆分该空白。但是,该代码很快变得非常臃肿。

我需要拆分的运算符是标准数学运算符(+,-,*,/,^),以及函数名(sin,cos,tan,abs,etc...)和逗号

这是一种快速,有效的方法吗?

3 个答案:

答案 0 :(得分:2)

我不知道这是否会完成你想要实现的所有,但它对我有用:

'sin(4+3)*2'.match(/\d+\.?\d*|[a-zA-Z]+|\S/g);

// ["sin", "(", "4", "+", "3", ")", "*", "2"]

您可以将[a-zA-Z]+部分替换为sin|cos|tan|etc以仅支持数学函数。

答案 1 :(得分:2)

您可以利用正则表达式分组来执行此操作。您需要一个结合了不同可能令牌的正则表达式,并重复应用它。

我喜欢把不同的部分分开;它使维护和扩展更容易:

var tokens = [
  "sin",
  "cos",
  "tan",
  "\\(",
  "\\)",
  "\\+",
  "-",
  "\\*",
  "/",
  "\\d+(?:\\.\\d*)?"
];

你将所有这些粘合成一个大的正则表达式,每个标记之间都有|

var rtok = new RegExp( "\\s*(?:(" + tokens.join(")|(") + "))\\s*", "g" );

然后,您可以使用源字符串上的正则表达式操作进行标记:

function tokenize( expression ) {
  var toks = [], p;

  rtok.lastIndex = p = 0; // reset the regex
  while (rtok.lastIndex < expression.length) {
    var match = rtok.exec(expression);

    // Make sure we found a token, and that we found
    // one without skipping garbage

    if (!match || rtok.lastIndex - match[0].length !== p)
      throw "Oops - syntax error";

    // Figure out which token we matched by finding the non-null group
    for (var i = 1; i < match.length; ++i) {
      if (match[i]) {
        toks.push({
          type: i,
          txt: match[i]
        });
        // remember the new position in the string
        p = rtok.lastIndex;
        break;
      }
    }
  }
  return toks;
}

只是将令牌正则表达式与字符串重复匹配。正则表达式是使用“g”标志创建的,因此正则表达式机制将自动跟踪我们在每次匹配后开始匹配的位置。当它没有看到匹配时,或者当它发生但却必须跳过无效的东西才能找到它时,我们知道存在语法错误。当它匹配时,它在令牌数组中记录它匹配的令牌(非空组的索引)和匹配的文本。通过记住匹配的令牌索引,您可以省去在标记后必须弄清楚每个令牌字符串的含义的麻烦;你只需做一个简单的数字比较。

因此,调用tokenize( "sin(4+3) * cos(25 / 3)" )会返回:

[ { type: 1, txt: 'sin' },
  { type: 4, txt: '(' },
  { type: 10, txt: '4' },
  { type: 6, txt: '+' },
  { type: 10, txt: '3' },
  { type: 5, txt: ')' },
  { type: 8, txt: '*' },
  { type: 2, txt: 'cos' },
  { type: 4, txt: '(' },
  { type: 10, txt: '25' },
  { type: 9, txt: '/' },
  { type: 10, txt: '3' },
  { type: 5, txt: ')' } ]

令牌类型1是sin函数,类型4是左paren,类型10是数字等等。

编辑 - 如果你想匹配像“x”和“y”这样的标识符,那么我可能会使用一组不同的令牌模式,其中一个只是为了匹配任何标识符。这意味着解析器不会直接从词法分析器中找到关于“sin”和“cos”等的内容,但这没关系。这是令牌模式的替代列表:

var tokens = [
  "[A-Za-z_][A-Za-z_\d]*",
  "\\(",
  "\\)",
  "\\+",
  "-",
  "\\*",
  "/",
  "\\d+(?:\\.\\d*)?"
];

现在任何标识符都是类型1标记。

答案 2 :(得分:1)

提供一些可能性:

[a-zA-Z]+|\d+(?:\.\d+)?|.