如何处理语法中的空格?
How to work with whitespaces within grammar?
我想为 LISP 的简化版本创建 parser/lexer。这是 bison/lexer 规格:
/* Lexer file */
"(" {return OP;}
")" {return CP;}
[0-9]+ {return NUM;}
["][a-zA-Z]*["] { return STR; }
[ \n\r\f] { /*do nothing*/}
. {return INVALID_TOKEN;}
/* Bison file */
start_expr: components_list
components_list : /*nothing*/
| components_list component
component : OP STR NUM CP
这样的字符串符合语法("f" 1) ("f"1)( "f" 1)( "f" 1 )
。但是表达式 ("f"1)
对我来说看起来很糟糕,我决定在语法中添加明确的定界符(使用 WHITESPACE
类型的标记 [ \n\r\f]+
)。类似的东西:
opt_wspace : /*nothing*/
| WHITESPACE
start_expr: components_list
components_list : /*nothing*/
| components_list component
component : OP opt_wspace STR WHITESPACE NUM opt_wspace CP
但是现在(对我而言)语法看起来很糟糕,但是种类 ("f"1)
的表达是不允许的。另一个时刻是,现在我很容易在语法上犯错误。例如,这样的表达式将不会被解析 ("f" 1) ("f" 1)
(我忘记在 components_list
中添加 opt_wspace
的用法)。
所以我的基本问题是如何在语法中使用 delimiters/whitespaces?我查看了 python (https://github.com/python/cpython/blob/master/Grammar/Grammar) 的语法,但似乎没有提到空格 expressions/tokens。这是次要引用:
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
[('=' (yield_expr|testlist_star_expr))+ [TYPE_COMMENT]] )
None 我知道的 lisps(或者一般的真正的编程语言)迫使你在这样的标记之间放置 spaces。例如,像 (display"hello")
或 (format t"~d"42)
这样的东西分别在 Scheme 和 Common Lisp 中工作得很好。所以你尝试做的事情并不常见,我建议你不要这样做。
就是说,如果您确实想在某些标记之间强制执行白色 space,您的两个选择是继续做您正在做的事情,或者为匹配任何序列的无效标记定义规则您想要禁止的令牌。像这样:
[0-9]+ {return NUM;}
["][^"]*["] { return STR; }
(["][^"]*["]|[0-9]+){2,} { return INVALID_TOKEN; }
因此,只要多个字符串或数字彼此相邻且中间没有任何内容,就会生成 INVALID_TOKEN
。随着您添加更多您不想允许彼此相邻的令牌类型(例如标识符),此模式将变得越来越复杂。
PS:只允许字符串中包含字母是很不寻常的,这就是为什么我在上面更改了字符串文字的正则表达式。您可能需要进一步调整它以允许在字符串中使用转义双引号。
我想为 LISP 的简化版本创建 parser/lexer。这是 bison/lexer 规格:
/* Lexer file */
"(" {return OP;}
")" {return CP;}
[0-9]+ {return NUM;}
["][a-zA-Z]*["] { return STR; }
[ \n\r\f] { /*do nothing*/}
. {return INVALID_TOKEN;}
/* Bison file */
start_expr: components_list
components_list : /*nothing*/
| components_list component
component : OP STR NUM CP
这样的字符串符合语法("f" 1) ("f"1)( "f" 1)( "f" 1 )
。但是表达式 ("f"1)
对我来说看起来很糟糕,我决定在语法中添加明确的定界符(使用 WHITESPACE
类型的标记 [ \n\r\f]+
)。类似的东西:
opt_wspace : /*nothing*/
| WHITESPACE
start_expr: components_list
components_list : /*nothing*/
| components_list component
component : OP opt_wspace STR WHITESPACE NUM opt_wspace CP
但是现在(对我而言)语法看起来很糟糕,但是种类 ("f"1)
的表达是不允许的。另一个时刻是,现在我很容易在语法上犯错误。例如,这样的表达式将不会被解析 ("f" 1) ("f" 1)
(我忘记在 components_list
中添加 opt_wspace
的用法)。
所以我的基本问题是如何在语法中使用 delimiters/whitespaces?我查看了 python (https://github.com/python/cpython/blob/master/Grammar/Grammar) 的语法,但似乎没有提到空格 expressions/tokens。这是次要引用:
stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | [('=' (yield_expr|testlist_star_expr))+ [TYPE_COMMENT]] )
None 我知道的 lisps(或者一般的真正的编程语言)迫使你在这样的标记之间放置 spaces。例如,像 (display"hello")
或 (format t"~d"42)
这样的东西分别在 Scheme 和 Common Lisp 中工作得很好。所以你尝试做的事情并不常见,我建议你不要这样做。
就是说,如果您确实想在某些标记之间强制执行白色 space,您的两个选择是继续做您正在做的事情,或者为匹配任何序列的无效标记定义规则您想要禁止的令牌。像这样:
[0-9]+ {return NUM;}
["][^"]*["] { return STR; }
(["][^"]*["]|[0-9]+){2,} { return INVALID_TOKEN; }
因此,只要多个字符串或数字彼此相邻且中间没有任何内容,就会生成 INVALID_TOKEN
。随着您添加更多您不想允许彼此相邻的令牌类型(例如标识符),此模式将变得越来越复杂。
PS:只允许字符串中包含字母是很不寻常的,这就是为什么我在上面更改了字符串文字的正则表达式。您可能需要进一步调整它以允许在字符串中使用转义双引号。