FsLex FsYacc:如何创建带有多行注释的语言
FsLex FsYacc: How to create a language with a multi-line comment
我正在玩弄基于 ocamllex 和 ocamlyacc 的 FsLex 和 FsYacc。用一种语言定义评论的最佳方式是什么?是否在我的 lex 文件中创建注释标记?评论有一些复杂的地方,我无法在语法的上下文中绕过:
- 注释可以按字面意思放在语法中的任何位置,应该被忽略。
- 评论中可以包含任何内容,包括其他标记和无效代码。
- 注释可以跨越多行,我需要为调试器维护源代码位置。在 FsLex 和 ocamllex 中,这必须由语言开发人员完成。
既然你包含了 ocaml
标签,我会回答 ocamllex
。
处理注释确实很困难,尤其是当您的语言希望能够注释掉代码段时。在这种情况下,注释词法分析器必须在 注释 中寻找(减少的一组)标记,以免被引用上下文中出现的注释闭包所愚弄。这也意味着词法分析器应该遵循注释的嵌套,所以注释掉的注释不会混淆。
OCaml 编译器本身就是这种方法的一个例子。 OCaml 编译器的注释处理分为三个部分。一级词法规则如下所示:
rule main = parse
. . . code omitted here . . .
| "(*"
{ comment_depth := 1;
handle_lexical_error comment lexbuf;
main lexbuf }
第二层由函数handle_lexical_error
和函数comment
组成。前者在捕获特定异常时评估词法分析函数。后者是注释的详细词法分析功能。在对注释进行词法分析之后,上面的代码返回到常规词法分析(main lexbuf
)。
函数 comment
如下所示:
rule comment = parse
"(*"
{ incr comment_depth; comment lexbuf }
| "*)"
{ decr comment_depth;
if !comment_depth = 0 then () else comment lexbuf }
| '"'
{ reset_string_buffer();
string lexbuf;
reset_string_buffer();
comment lexbuf }
| "'"
{ skip_char lexbuf ;
comment lexbuf }
| eof
{ raise(Lexical_error("unterminated comment", "", 0, 0)) }
| '0'
{ incr_loc lexbuf 0;
comment lexbuf }
| _
{ comment lexbuf }
所以,是的,做好工作非常复杂。
对于最后一点,ocamllex
会自动为您跟踪源代码位置。您可以从 lexbuf 中检索它们。请参阅 OCaml Lexing
模块。 (但是,请注意,上面的注释词法分析函数在词法换行时会调整位置。incr_loc
函数会增加跟踪的行号。)
我不确定 F# 对这种设计的跟踪程度如何,但希望这会有所帮助。
更新
这是 string
词法分析函数:
rule string = parse
'"'
{ () }
| '\' ("0" | "3" | "30") ([' ' '[=12=]9'] * as spaces)
{ incr_loc lexbuf (String.length spaces);
string lexbuf }
| '\' (backslash_escapes as c)
{ store_string_char(char_for_backslash c);
string lexbuf }
| '\' (['0'-'9'] as c) (['0'-'9'] as d) (['0'-'9'] as u)
{ let v = decimal_code c d u in
if in_pattern () && v > 255 then
warning lexbuf
(Printf.sprintf
"illegal backslash escape in string: `\%c%c%c'" c d u) ;
store_string_char (Char.chr v);
string lexbuf }
| '\' 'x' (['0'-'9' 'a'-'f' 'A'-'F'] as d) (['0'-'9' 'a'-'f' 'A'-'F'] as u)
{ store_string_char (char_for_hexadecimal_code d u) ;
string lexbuf }
| '\' (_ as c)
{if in_pattern () then
warning lexbuf
(Printf.sprintf "illegal backslash escape in string: `\%c'" c) ;
store_string_char '\' ;
store_string_char c ;
string lexbuf }
| eof
{ raise(Lexical_error("unterminated string", "", 0, 0)) }
| '0'
{ store_string_char '0';
incr_loc lexbuf 0;
string lexbuf }
| _ as c
{ store_string_char c;
string lexbuf }
如果您想了解更多信息,可以在此处找到完整的 OCaml 词法分析器源代码:lexer.mll.
我正在玩弄基于 ocamllex 和 ocamlyacc 的 FsLex 和 FsYacc。用一种语言定义评论的最佳方式是什么?是否在我的 lex 文件中创建注释标记?评论有一些复杂的地方,我无法在语法的上下文中绕过:
- 注释可以按字面意思放在语法中的任何位置,应该被忽略。
- 评论中可以包含任何内容,包括其他标记和无效代码。
- 注释可以跨越多行,我需要为调试器维护源代码位置。在 FsLex 和 ocamllex 中,这必须由语言开发人员完成。
既然你包含了 ocaml
标签,我会回答 ocamllex
。
处理注释确实很困难,尤其是当您的语言希望能够注释掉代码段时。在这种情况下,注释词法分析器必须在 注释 中寻找(减少的一组)标记,以免被引用上下文中出现的注释闭包所愚弄。这也意味着词法分析器应该遵循注释的嵌套,所以注释掉的注释不会混淆。
OCaml 编译器本身就是这种方法的一个例子。 OCaml 编译器的注释处理分为三个部分。一级词法规则如下所示:
rule main = parse
. . . code omitted here . . .
| "(*"
{ comment_depth := 1;
handle_lexical_error comment lexbuf;
main lexbuf }
第二层由函数handle_lexical_error
和函数comment
组成。前者在捕获特定异常时评估词法分析函数。后者是注释的详细词法分析功能。在对注释进行词法分析之后,上面的代码返回到常规词法分析(main lexbuf
)。
函数 comment
如下所示:
rule comment = parse
"(*"
{ incr comment_depth; comment lexbuf }
| "*)"
{ decr comment_depth;
if !comment_depth = 0 then () else comment lexbuf }
| '"'
{ reset_string_buffer();
string lexbuf;
reset_string_buffer();
comment lexbuf }
| "'"
{ skip_char lexbuf ;
comment lexbuf }
| eof
{ raise(Lexical_error("unterminated comment", "", 0, 0)) }
| '0'
{ incr_loc lexbuf 0;
comment lexbuf }
| _
{ comment lexbuf }
所以,是的,做好工作非常复杂。
对于最后一点,ocamllex
会自动为您跟踪源代码位置。您可以从 lexbuf 中检索它们。请参阅 OCaml Lexing
模块。 (但是,请注意,上面的注释词法分析函数在词法换行时会调整位置。incr_loc
函数会增加跟踪的行号。)
我不确定 F# 对这种设计的跟踪程度如何,但希望这会有所帮助。
更新
这是 string
词法分析函数:
rule string = parse
'"'
{ () }
| '\' ("0" | "3" | "30") ([' ' '[=12=]9'] * as spaces)
{ incr_loc lexbuf (String.length spaces);
string lexbuf }
| '\' (backslash_escapes as c)
{ store_string_char(char_for_backslash c);
string lexbuf }
| '\' (['0'-'9'] as c) (['0'-'9'] as d) (['0'-'9'] as u)
{ let v = decimal_code c d u in
if in_pattern () && v > 255 then
warning lexbuf
(Printf.sprintf
"illegal backslash escape in string: `\%c%c%c'" c d u) ;
store_string_char (Char.chr v);
string lexbuf }
| '\' 'x' (['0'-'9' 'a'-'f' 'A'-'F'] as d) (['0'-'9' 'a'-'f' 'A'-'F'] as u)
{ store_string_char (char_for_hexadecimal_code d u) ;
string lexbuf }
| '\' (_ as c)
{if in_pattern () then
warning lexbuf
(Printf.sprintf "illegal backslash escape in string: `\%c'" c) ;
store_string_char '\' ;
store_string_char c ;
string lexbuf }
| eof
{ raise(Lexical_error("unterminated string", "", 0, 0)) }
| '0'
{ store_string_char '0';
incr_loc lexbuf 0;
string lexbuf }
| _ as c
{ store_string_char c;
string lexbuf }
如果您想了解更多信息,可以在此处找到完整的 OCaml 词法分析器源代码:lexer.mll.