Ocaml 更改Lexing.lexbuf的状态
我正在用ocamlex编写一个lexer,为了实现它的循环,我需要更改lexbuf的状态,以便它可以返回到流中的前一个位置 脑力操的背景信息(可跳过) 在Brainfuck中,一个循环是由一对方括号和 以下规则:Ocaml 更改Lexing.lexbuf的状态,ocaml,lex,ocamllex,Ocaml,Lex,Ocamllex,我正在用ocamlex编写一个lexer,为了实现它的循环,我需要更改lexbuf的状态,以便它可以返回到流中的前一个位置 脑力操的背景信息(可跳过) 在Brainfuck中,一个循环是由一对方括号和 以下规则: [->继续并评估下一个令牌 ]->如果当前单元格的值不是0,则返回到匹配的[ 因此,以下代码的计算结果为15: +++ [ > +++++ < - ] > . 其他规则工作正常,但似乎忽略了[和]。问题显然在于loopstack以及我如何获取和设置lex\u c
->继续并评估下一个令牌[
->如果当前单元格的值不是0,则返回到匹配的]
[
+++ [ > +++++ < - ] > .
其他规则工作正常,但似乎忽略了
[
和]
。问题显然在于loopstack
以及我如何获取和设置lex\u curr\u p
状态。如果有任何线索,我将不胜感激。lex\u curr\u p
的目的是跟踪当前位置,以便您可以在错误消息等中使用它。将其设置为新值不会使lexer实际返回到当前位置文件中较早的位置。事实上,我99%确信,无论你做什么,你都不能使lexer循环像那样
因此,您不能像您正在尝试的那样使用ocamlex
来实现整个解释器。您可以做的(以及ocamlex的设计目的)是将输入的字符流转换为令牌流
在其他语言中,这意味着将字符流(如var xyz=/*comment*/123
转换为标记流(如var,ID(“xyz”)、EQ,INT(123)
),因此词法分析有三种帮助:它找到一个标记的结束和下一个标记的开始,它将标记分类为不同的类型(标识符与关键字等)并丢弃您不需要的令牌(空白和注释)。这可以大大简化进一步的处理
对Brainfuck进行词法分析的帮助要小得多,因为所有Brainfuck标记都只包含一个字符。因此,找出每个标记的结束位置和下一个标记的开始位置是不可行的,找出标记的类型只意味着将字符与[',“+”等。所以脑力操词法分析器做的唯一有用的事情就是丢弃空格和注释
因此,你的词法分析器要做的是将输入[,[+-.lala comment]>]
转换成类似循环开始,进入,循环开始,INC,DEC,OUT,循环结束,向右移动,循环结束
,其中循环开始
等属于你(或你的解析器生成器,如果你使用的话)的一个有区别的联合体定义并生成lexer输出
如果您想使用解析器生成器,您需要在解析器的语法中定义令牌类型,并使lexer生成这些类型的值,然后解析器就可以解析令牌流
如果你想手工解析,你可以在一个循环中手工调用lexer的token
函数来获取所有的token。为了实现循环,你必须将已经消耗的token存储在某个地方,以便能够进行循环。最终,这将不仅仅是将输入读入字符串,而是为了学习因为我想这没关系
这就是说,我建议您一直使用解析器生成器来创建AST。这样,您就不必为循环创建令牌缓冲区,让AST实际为您节省一些工作(您不再需要堆栈来跟踪哪个
[
属于哪个]
).lex\u curr\u p
旨在跟踪当前位置,以便您可以在错误消息等中使用它。将其设置为新值不会使lexer实际查找回文件中的早期位置。事实上,我99%确定,无论您做什么,都无法使lexer像这样循环
因此,您不能像您正在尝试的那样使用ocamlex
来实现整个解释器。您可以做的(以及ocamlex的设计目的)是将输入的字符流转换为令牌流
在其他语言中,这意味着将字符流(如var xyz=/*comment*/123
转换为标记流(如var,ID(“xyz”)、EQ,INT(123)
),因此词法分析有三种帮助:它找到一个标记的结束和下一个标记的开始,它将标记分类为不同的类型(标识符与关键字等)并丢弃您不需要的令牌(空白和注释)。这可以大大简化进一步的处理
对Brainfuck进行词法分析的帮助要小得多,因为所有Brainfuck标记都只包含一个字符。因此,找出每个标记的结束位置和下一个标记的开始位置是不可行的,找出标记的类型只意味着将字符与[',“+”等。所以脑力操词法分析器做的唯一有用的事情就是丢弃空格和注释
因此,你的词法分析器要做的是将输入[,[+-.lala comment]>]
转换成类似循环开始,进入,循环开始,INC,DEC,OUT,循环结束,向右移动,循环结束
,其中循环开始
等属于你(或你的解析器生成器,如果你使用的话)的一个有区别的联合体定义并生成lexer输出
如果您想使用解析器生成器,您需要在解析器的语法中定义令牌类型,并使lexer生成这些类型的值,然后解析器就可以解析令牌流
如果你想手工解析,你可以在一个循环中手工调用lexer的token
函数来获取所有的token。为了实现循环,你必须将已经消耗的token存储在某个地方,以便能够进行循环。最终,这将不仅仅是将输入读入字符串,而是为了学习因为我想这没关系
也就是说,我建议您一直使用解析器生成器来创建AST。这样您就不必为循环创建令牌缓冲区,使用AST实际上可以节省一些工作(您不需要)
{ (* Header *)
let tape = Array.make 100 0
let tape_pos = ref 0
let loopstack = ref []
let push_curr_p (lexbuf: Lexing.lexbuf) =
let p = lexbuf.Lexing.lex_curr_p in
let curr_pos = p.Lexing.pos_cnum in
(* Saving / pushing the position of `[` to loopstack *)
( loopstack := curr_pos :: !loopstack
; lexbuf
)
let pop_last_p (lexbuf: Lx.lexbuf) =
match !loopstack with
| [] -> lexbuf
| hd :: tl ->
(* This is where I attempt to bring lexbuf back *)
( lexbuf.Lexing.lex_curr_p <- { lexbuf.Lexing.lex_curr_p with Lexing.pos_cnum = hd }
; loopstack := tl
; lexbuf
)
}
{ (* Rules *)
rule brainfuck = parse
| '[' { brainfuck (push_curr_p lexbuf) }
| ']' { (* current cell's value must be 0 to exit the loop *)
if tape.(!tape_pos) = 0
then brainfuck lexbuf
(* this needs to bring lexbuf back to the previous `[`
* and proceed with the parsing
*)
else brainfuck (pop_last_p lexbuf)
}
(* ... other rules ... *)
}