解析Javascript时,什么决定了斜杠的含义?
Javascript有一个很难解析的语法。正斜杠可以表示许多不同的内容:除法运算符、正则表达式文字、注释引入器或行注释引入器。最后两个很容易区分:如果斜杠后面跟一个星号,它将开始一个多行注释。如果斜杠后面跟着另一个斜杠,则它是一个行注释 但是除法和正则表达式文字的消歧规则让我不知所措。我在房间里找不到它。根据斜杠的含义,词汇语法被明确地分为两部分:InputElementDiv和InputElementRegExp。但并没有解释什么时候该用哪个 当然,可怕的分号插入规则使一切变得复杂 有没有人有一个清晰的代码示例,可以解释Javascript的词法?参见第7节: 词汇语法有两个目标符号。InputElementDiv符号用于允许前导除法(/)或除法赋值(/=)运算符的语法上下文中。InputElementRegExp符号用于其他语法上下文 注意,不存在允许前导除法或除法赋值以及前导RegularExpressionLiteral的语法上下文。这不受分号插入的影响(见7.9);例如 以下:解析Javascript时,什么决定了斜杠的含义?,javascript,lexer,Javascript,Lexer,Javascript有一个很难解析的语法。正斜杠可以表示许多不同的内容:除法运算符、正则表达式文字、注释引入器或行注释引入器。最后两个很容易区分:如果斜杠后面跟一个星号,它将开始一个多行注释。如果斜杠后面跟着另一个斜杠,则它是一个行注释 但是除法和正则表达式文字的消歧规则让我不知所措。我在房间里找不到它。根据斜杠的含义,词汇语法被明确地分为两部分:InputElementDiv和InputElementRegExp。但并没有解释什么时候该用哪个 当然,可怕的分号插入规则使一切变得复杂 有没有人有
a = b
/hi/g.exec(c).map(d);
如果行终止符后的第一个非空白、非注释字符是斜杠(/),并且语法上下文允许除法或除法赋值,则不会在行终止符处插入分号。也就是说,上面的示例在中进行了解释
与以下方法相同:
a = b / hi / g.exec(c).map(d);
我同意,这很混乱,应该有一个顶级语法表达式而不是两个
编辑: 但并没有解释什么时候该用哪个
也许简单的答案就在眼前:试一个,然后再试另一个。由于不允许两者都使用,因此最多只能产生一个无错误的匹配。您只能通过实现语法分析器来了解如何解释/。任何到达有效解析的lex路径都决定了如何解释字符。显然,这是他们曾考虑过解决的问题,但没有。 更多阅读:
如果前面的标记是
(,=:[!&|?{};
Rhino总是从lexer返回一个DIV令牌。这实际上相当简单,但它需要让你的lexer比平常更聪明一点 除法运算符必须跟在表达式后面,而正则表达式文字不能跟在表达式后面,因此在所有其他情况下,可以安全地假设您正在查看正则表达式文字 如果操作正确,您已经必须将标点符号标识为多个字符串。因此,请查看前面的标记,并查看它是否为以下任何标记:
. ( , { } [ ; , < > <= >= == != === !== + - * % ++ --
<< >> >>> & | ^ ! ~ && || ? : = += -= *= %= <<= >>= >>>=
&= |= ^= / /=
如果您刚才使用的IdentifierName是其中之一,那么您将看到一个正则表达式文本;否则,它就是一个除法器
以上内容基于ECMAScript 5.1规范(已找到),不包括任何特定于浏览器的语言扩展。但如果您需要支持这些,那么这应该为确定您所处的环境类型提供简单的指导
当然,上面的大多数都是包含正则表达式文本的非常愚蠢的情况。例如,即使在语法上允许,也不能对正则表达式进行预增量。因此,大多数工具都可以简化实际应用程序的正则表达式上下文检查。JSLint检查前一个字符是否为
(,=:[!&|?{}的方法
可能就足够了。但是如果你在开发本应是JS词法分析工具的工具时走了这样一条捷径,那么你应该一定要注意。我目前正在用JavaCC开发一个。这两件事让我对ECMAScript语法发疯。这个问题和答案对于正则表达式问题来说是非常宝贵的。在这个答案我想把我自己的发现放在一起
TL;DR在JavaCC中,使用和
非常重要的是Thom Blake所写的: 除法运算符必须跟在表达式和正则表达式之后 表达式文字不能跟在表达式后面,所以在所有其他情况下 您可以放心地假设您正在查看正则表达式文本 因此,您实际上需要了解它之前是否是一个表达式。这在解析器中是微不足道的,但在lexer中是非常困难的
Thom,在许多(但不幸的是,并非全部)的情况下,你可以理解它是“看”最后一个令牌的表达式。你必须考虑标点符号和关键字。
让我们从关键字开始。以下关键字不能在标点符号或
之前(例如,您不能有大小写/5
),因此,如果您在这些关键字之后看到/
,您就有了RegularExpressionLiteral
:
case
delete
do
else
in
instanceof
new
return
throw
typeof
void
if ('a')/a/g
接下来是标点符号。以下标点符号不能位于除法标点符号之前(例如在{/a..
中,符号/
不能开始除法):
然后它也必须是一个正则表达式文字。如果这些斜杠之间没有空格(即/…
),那么它必须作为单行注释处理()
接下来,以下标点符号可能仅用于结束表达式:
]
因此,下面的/
必须启动除法器
不幸的是,我们还有以下几起案件模棱两可:
}
)
++
--
对于}
和)
您必须知道它们是否结束表达式,对于++
和--
-它们结束后固定表达式
或启动无固定表达式
]
}
)
++
--
{}/a/g
+{}/a/g
('a')/a/g
if ('a')/a/g