如何处理语法中的空格?

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:只允许字符串中包含字母是很不寻常的,这就是为什么我在上面更改了字符串文字的正则表达式。您可能需要进一步调整它以允许在字符串中使用转义双引号。