1

我正在尝试在生成的解析器中使用Menhir增量解析 API自省 API 。比如说,我想确定与特定 LR(1) 堆栈条目相关的语义值;即解析器先前使用的令牌。

给定一个抽象的解析检查点,封装在 Menhir 的 type'a env中,我可以从 LR 自动机中提取一个“堆栈元素”;它看起来像这样:

type element =
  | Element: 'a lr1state * 'a * position * position -> element

type 元素描述了 LR(1) 自动机堆栈中的一个条目。在形式的堆栈元素中Element (s, v, startp, endp)s是(非初始)状态并且v是语义值。该值v与状态的传入符号 A 相关联s。换句话说,值在进入v状态之前被压入堆栈s。因此,对于某些 type 'a, states有 type'a lr1state并且 valuev有 type 'a...

为了对值做任何有用的事情,必须通过检查状态v来获取有关类型的信息。到目前为止,类型是抽象的,所以没有办法检查. 检查 API(第 9.3 节)为此提供了更多工具。'as'a lr1states

好,爽!所以我开始深入检查 API:

类型 'a 终端是一种广义代数数据类型 (GADT)。'a 终端类型的值表示终端符号(没有语义值)。索引 'a 是与此符号关联的语义值的类型...

type _ terminal =
| T_A : unit terminal
| T_B : int terminal

类型 'a 非终结符也是一个 GADT。'a 非终结符类型的值表示非终结符符号(没有语义值)。索引 'a 是与此符号关联的语义值的类型...

type _ nonterminal =
| N_main : thing nonterminal

将这些拼凑在一起,我得到如下内容(其中“命令”是我的语法的非终结符之一,因此N_command是 a string nonterminal):

let current_command (env : 'a env) =
   let rec f i =
      match Interpreter.get i env with
      | None -> None
      | Some Interpreter.Element (lr1state, v, _startp, _endp) ->
      match Interpreter.incoming_symbol lr1state with
      | Interpreter.N Interpreter.N_command -> Some v
      | _ -> f (i + 1)
   in
   f 0

不幸的是,这对我来说是非常令人困惑的类型错误:

File "src/incremental.ml", line 110, characters 52-53:
Error: This expression has type string but an expression was expected of type
         string
       This instance of string is ambiguous:
       it would escape the scope of its equation

这有点超出我的水平!我很确定我明白为什么我不能做我上面试图做的事情;但我不明白我的选择是什么。事实上,Menhir 手册特别提到了这种复杂性:

此函数可用于访问v堆栈元素中的语义值Element (s, v, _, _)。事实上,通过对符号的案例分析incoming_symbol s,人们获得了关于类型的信息'a,因此获得了对值做一些有用的事情的能力v

好的,但这就是我认为我所做的,上面:通过match'ing on进行案例分析incoming_symbol s,拉出v单个特定类型的案例:string.

tl;博士:我如何从这个 GADT 中提取字符串有效负载,并用它做一些有用的事情?

4

1 回答 1

2

如果你的错误听起来像

这个字符串的实例是模棱两可的:它会逃脱其方程的范围

这意味着类型检查器不确定是否在模式匹配分支之外的类型v应该是一个字符串,或者另一个等于string但仅在分支内部的类型。您只需要在离开分支时添加类型注释即可消除这种歧义:

 | Interpreter.(N N_command) -> Some (v:string)
于 2019-08-11T18:18:25.943 回答