如何 lex/tokenise 模板文字

How to lex/tokenise template literals

我正在对一种基于 javascript 的自定义语言进行词法分析(转译为 JS),并且在如何构建(与 js 相同的)模板文字方面感到困惑。

目前我将字符串作为一个完整的标记(包括转义符)进行 lex,但由于嵌入的表达式,我无法对模板字符串执行此操作。 该语言允许在一行中包含多个语句(即忽略空格),例如:let foo = {} let bar = `a template literal with embedded ${foo} expression`

我可以创建一个匹配反引号的标记,但随后我需要创建一个匹配“字符串文本”的标记,其中包括转义符等(不与其他表达式标记冲突)。 我希望与正则表达式 ([`}])[^`\$\n]*(`|$\{) 匹配(为简洁起见省略了转义符),但是当在对象之后声明时(如示例中所示),由于明显的原因,这会失败。

作为参考,我正在使用 rust logos 库来标记化,但我认为这更多的是不知道如何开始实现标记的问题,而不是特定的库。

为了像

这样对模板字符串进行词法分析
let bar = `a template literal with embedded ${foo} expression`

您需要一个允许多个扫描仪状态的扫描仪生成器。因为模板内部的词法上下文与程序其余部分的上下文非常不同。

这是一个非常普遍的特性 -- lex 基本上一直都有它 -- 但我对你正在使用的扫描仪生成器一点也不熟悉,而且易于搜索的文档不够清晰,我无法判断是否它实现了状态,更不用说使用它们的语法了。

这个问题你只需要两个状态,但由于语法是递归的,你需要用某种状态堆栈来支持它。

基本上,你有你的正常状态,在这种状态下,语言中的每个标记都有规则。其中一个标记将是 `,它将当前状态推入状态堆栈并更改为“内部模板”状态。唯一的其他修改是 {} 需要压入和弹出堆栈。 ({ 将当前状态压入堆栈但不改变状态。)

在“inside template”状态下,只有几种token类型:

  • `:弹出状态栈(和returns一个`令牌)
  • ${压入状态栈,切换到正常状态
  • 任何其他序列作为部分字符串发送。

这是一个非常粗略的轮廓。