如何在Javascript中实现词法分析

16

大家好,感谢阅读。

我正在尝试做一个类似 Google 的计算器。你输入一个字符串,它会确定是否可以计算并返回结果。

我从基础开始慢慢构建:+ - / * 和括号处理。

我希望随着时间的推移改进计算器,并且在一段时间前学习了一些词法分析的知识,我构建了一个令牌列表和相关的正则表达式模式。

这种工作在像 Lex 和 Yacc 这样的语言中很容易应用,但是我正在开发一个仅使用 Javascript 的应用程序。

我试图将想法转录到 Javascript 中,但我无法找出如何以清晰美观的方式处理所有内容,特别是嵌套的括号。


分析

让我们定义什么是计算器查询:

// NON TERMINAL EXPRESSIONS //
query     -> statement
query     -> ε // means end of query

statement -> statement operator statement
statement -> ( statement )
statement -> prefix statement
statement -> number

number    -> integer
number    -> float

// TERMINAL EXPRESSIONS //
operator  -> [+*/%^-]

prefix    -> -

integer   -> [0-9]+

float     -> [0-9]+[.,][0-9]+

Javascript

词法分析主要是验证是否存在不符合运算符、前缀、整数和浮点数终极表达式之一的内容。这可以简化为一个正则表达式:

(我添加了空格以使其更易读)

var calcPat = 
/^ (\s*
    ( ([+/*%^-]) | ([0-9]+) | ([0-9]+[.,][0-9]+) | (\() | (\)) )
)+ \s* $/;
如果这个测试通过,查询就在词法上正确,需要进行语法检查以确定是否可以计算。这是棘手的部分。
我不会粘贴代码,因为它不干净也不易理解,但我会解释我遵循的过程以及我卡住的原因:
我创建了一个名为 isStatement(string) 的方法,该方法应该递归调用自身。主要想法是将字符串拆分为“潜在”语句,然后检查它们是否真正是语句并组成一个整体。 过程如下:
- 如果前两个标记是数字后跟运算符: - 然后, -- 如果剩余的只有一个标记,并且它是一个数字: --- 那么这是一个语句。 --- 否则,请检查剩余的标记是否形成语句(递归调用)
-否则,如果第一个标记是括号 -然后,找到匹配的右括号并检查括号内是否为语句(递归) --还要检查右括号后是否有内容,并且如果与括号结构相关联,则是否构成语句。
问题是什么?
我的问题是当存在嵌套结构时,我无法找到匹配的括号。我该怎么做? 此外,正如您所看到的,这不是特别通用和清晰的语法检查算法。您有任何改进此模式的想法吗?
非常感谢您抽出时间阅读所有内容。 Gael
(PS:正如您可能已经注意到的,我不是以英语为母语的人!很抱歉有错别字!)

2
你可能想试试这个工具:http://pegjs.majda.cz/online - Bart Kiers
这很酷,但(从它的名称来看)它生成 PEG 解析器(我猜是 Packrat 解析器),这实际上是完全不同的东西。经典的“词法分析器+语法分析器”方法用于构建 LL 或 LR 解析器,用于上下文无关文法(或几乎上下文无关),而 PEG 描述了与 CFG 不同的语言类别。 - Pointy
哦,特别值得注意的是,使用Packrat解析器处理PEG语言时,根本不需要词法分析器 :-) - Pointy
@bart-kiers:太棒了,感谢你的工具。几年前我试图自己构建一个解析器,但最终放弃了(幸运的是,那只是个爱好,而不是任何工作要求)。也许我需要重新开始这个项目了。 - keithjgrant
@bart-kiers:感谢提供链接,我已经成功构建了一个工作解析器,但由于我不知道它是如何工作的以及其他法律问题,我不能像那样在他们的背后赚钱! - Gabriel S.
1个回答

10
你已经对词法分析有了正确的想法,但你好像混淆了标记语法语言语法之间的区别。它们是两个不同的概念。
  • 标记语法是描述待解析语言的标记模式(通常是正则表达式)的集合。这些正则表达式是字符集上的表达式。

  • 语言语法(或目标语法,我猜)是您想要解析的语言的语法。 该语法是以标记为单位表达的。

您无法编写一个正则表达式来解析代数符号。你做不到。你可以写一个与你所拥有的类似的正则表达式来识别单独的标记。但是这不是一个常规语法。你需要做的是识别出每个单独的标记,而这可以通过类似于你所拥有的正则表达式来完成。关键在于你不应该将这个表达式应用于整个待解析句子。相反,你要匹配当前句子中的标记。
现在,在你拥有Javascript正则表达式的情况下,你可能可以设计一个正则表达式来匹配一串标记。但是需要注意的是,你需要找到一种方法来确定已匹配的标记是列表中的哪个标记。Javascript正则表达式引擎可以返回组的数组,因此你可能可以在此基础上构建一些东西。 编辑 - 我想尝试构建一个(有点)通用的标记生成器,从单独的正则表达式列表(每个标记一个正则表达式)开始。这可能不是非常复杂,并且它将会很有趣。

否则,你会陷入经典答案的陷阱RegEx match open tags except XHTML self-contained tags - Marcel Korpel
@Pointy 谢谢你抽出时间来。我思考并搜索有关解析器和标记的信息时,偶然发现了这篇 D. Crockford 的文章,我记得很久以前曾读过但完全不明白。现在我对它有了更深刻的理解。你觉得是否值得深入研究并尝试构建自己的解析器?http://javascript.crockford.com/tdop/tdop.html - Gabriel S.
@Pointy,我终于使用我上面发布的链接构建了自己的解析器。想到我可以使用它来解析JavaScript本身是相当惊人的,并且确实非常有趣!谢谢! - Gabriel S.
很酷,我很高兴它能够正常工作。昨天我花了一个小时左右编写了一个简单的函数,用于从一组标记正则表达式生成词法分析器对象。这是一个有趣的项目,尽管我不知道是否会再次使用这段代码 :-) - Pointy
@Gaël - 你可能会对这个项目感兴趣:https://github.com/aaditmshah/lexer 它可以从一组规则(模式+操作)生成动态词法分析器。它还支持起始条件、多个返回值和全局模式。 - Aadit M Shah
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接