我如何在bison + flex中将缩进实现为块分隔符。就像在python中一样。我正在编写自己的编程语言(主要是为了好玩,但我打算将它与游戏引擎一起使用),我将尝试提出一些特殊的东西,以最小化样板并最大化开发速度。
我已经在C中编写了一个编译器(实际上是`langToy'到Nasm翻译器)但是失败了。由于某种原因,它只能处理整个源文件中的一个字符串(好吧,我已经醒了超过48小时 - 所以...你知道,脑子崩溃了。)
我不知道大括号和/或是否开始 - >结束更容易实现(我没有问题)或者只是我的大脑锁定。
提前致谢!
更新:好的,我对如何使用flex进行操作一无所知。将多个DEDENT返回给解析器时遇到问题。 Flex / Bison对我来说相对较新。
更新2: 这是我到目前为止提出的flex文件;它不太明白:
%x t
%option noyywrap
%{
int lineno = 0, ntab = 0, ltab = 0, dedent = 0;
%}
%%
<*>\n { ntab = 0; BEGIN(t); }
<t>\t { ++ntab; }
<t>. { int i; /* my compiler complains not c99 if i use for( int i=0... */
if( ntab > ltab )
printf("> indent >\n");
else if( ntab < ltab )
for( i = 0; i < ltab - ntab; i++ )
printf("< dedent <\n");
else
printf("= =\n");
ltab = ntab; ntab = 0;
BEGIN(INITIAL);
/* move to next rule */
REJECT;}
. /* ignore everything else for now */
%%
main()
{
yyin = fopen( "test", "r" );
yylex();
}
你可以试着玩弄它,也许你会看到我所缺少的东西。返回多个dedents对Haxe来说很容易(返回t_dedent(num);)。
此代码并不总是与缩进/ dedent正确匹配。
更新3:我认为我会放弃对flex的希望并按照自己的方式行事,如果有人知道如何在flex中做到这一点,我会很高兴听到它。
答案 0 :(得分:14)
你需要做的是在每一行的开头用flex计算空白量,并插入适当数量的INDENT / UNINDENT令牌,供解析器用来分组。一个问题是你想要做什么关于标签与空格 - 你只是想让它们与固定制表位相同,或者你是否想要缩进是一致的(所以如果一行开头是一个标签和下一个如果有空格,则表示错误,这可能有点困难。
假设你想要固定的8列tabstops,你可以使用像
这样的东西%{
/* globals to track current indentation */
int current_line_indent = 0; /* indentation of the current line */
int indent_level = 0; /* indentation level passed to the parser */
%}
%x indent /* start state for parsing the indentation */
%s normal /* normal start state for everything else */
%%
<indent>" " { current_line_indent++; }
<indent>"\t" { current_line_indent = (current_line_indent + 8) & ~7; }
<indent>"\n" { current_line_indent = 0; /*ignoring blank line */ }
<indent>. {
unput(*yytext);
if (current_line_indent > indent_level) {
indent_level++;
return INDENT;
} else if (current_line_indent < indent_level) {
indent_level--;
return UNINDENT;
} else {
BEGIN normal;
}
}
<normal>"\n" { current_line_indent = 0; BEGIN indent; }
... other flex rules ...
您必须确保以缩进模式启动解析(以获取第一行的缩进)。
答案 1 :(得分:5)
克里斯的答案对于一个可用的解决方案有很长的路要走,感谢一大堆! 不幸的是,它缺少了我需要的一些更重要的方面:
同时有多个突发事件(无意)。在调用baz
后,请考虑以下代码应发出两个异常值:
def foo():
if bar:
baz()
在达到文件末尾但仍处于某个缩进级别时发出异常。
基于Chris的代码,我提出了一个解决方案,该解决方案适用于我目前遇到的所有情况。我创建了一个模板项目,用于在github:https://github.com/lucasb-eyer/flex-bison-indentation上使用flex(和bison)解析基于缩进的文本。它是一个完全工作(基于CMake)的项目,它还跟踪当前令牌的行位置和列范围。
以防链接因任何原因而中断,这是词法分析员的主要内容:
#include <stack>
int g_current_line_indent = 0;
std::stack<size_t> g_indent_levels;
int g_is_fake_outdent_symbol = 0;
static const unsigned int TAB_WIDTH = 2;
#define YY_USER_INIT { \
g_indent_levels.push(0); \
BEGIN(initial); \
}
#include "parser.hh"
%}
%x initial
%x indent
%s normal
%%
int indent_caller = normal;
/* Everything runs in the <normal> mode and enters the <indent> mode
when a newline symbol is encountered.
There is no newline symbol before the first line, so we need to go
into the <indent> mode by hand there.
*/
<initial>. { set_yycolumn(yycolumn-1); indent_caller = normal; yyless(0); BEGIN(indent); }
<initial>\n { indent_caller = normal; yyless(0); BEGIN(indent); }
<indent>" " { g_current_line_indent++; }
<indent>\t { g_current_line_indent = (g_current_line_indent + TAB_WIDTH) & ~(TAB_WIDTH-1); }
<indent>\n { g_current_line_indent = 0; /* ignoring blank line */ }
<indent><<EOF>> {
// When encountering the end of file, we want to emit an
// outdent for all indents currently left.
if(g_indent_levels.top() != 0) {
g_indent_levels.pop();
// See the same code below (<indent>.) for a rationale.
if(g_current_line_indent != g_indent_levels.top()) {
unput('\n');
for(size_t i = 0 ; i < g_indent_levels.top() ; ++i) {
unput(' ');
}
} else {
BEGIN(indent_caller);
}
return TOK_OUTDENT;
} else {
yyterminate();
}
}
<indent>. {
if(!g_is_fake_outdent_symbol) {
unput(*yytext);
}
g_is_fake_outdent_symbol = 0;
// -2: -1 for putting it back and -1 for ending at the last space.
set_yycolumn(yycolumn-1);
// Indentation level has increased. It can only ever
// increase by one level at a time. Remember how many
// spaces this level has and emit an indentation token.
if(g_current_line_indent > g_indent_levels.top()) {
g_indent_levels.push(g_current_line_indent);
BEGIN(indent_caller);
return TOK_INDENT;
} else if(g_current_line_indent < g_indent_levels.top()) {
// Outdenting is the most difficult, as we might need to
// outdent multiple times at once, but flex doesn't allow
// emitting multiple tokens at once! So we fake this by
// 'unput'ting fake lines which will give us the next
// outdent.
g_indent_levels.pop();
if(g_current_line_indent != g_indent_levels.top()) {
// Unput the rest of the current line, including the newline.
// We want to keep it untouched.
for(size_t i = 0 ; i < g_current_line_indent ; ++i) {
unput(' ');
}
unput('\n');
// Now, insert a fake character indented just so
// that we get a correct outdent the next time.
unput('.');
// Though we need to remember that it's a fake one
// so we can ignore the symbol.
g_is_fake_outdent_symbol = 1;
for(size_t i = 0 ; i < g_indent_levels.top() ; ++i) {
unput(' ');
}
unput('\n');
} else {
BEGIN(indent_caller);
}
return TOK_OUTDENT;
} else {
// No change in indentation, not much to do here...
BEGIN(indent_caller);
}
}
<normal>\n { g_current_line_indent = 0; indent_caller = YY_START; BEGIN(indent); }
答案 2 :(得分:1)
如果使用去除所有空格的标记生成器(仅使用单独的标记),则使用括号(等等)只会更简单。有关python标记化的一些想法,请参阅this page(“编译器如何解析缩进?”部分)。
如果您在解析之前没有进行标记化,那么可能还有其他工作要做,这取决于您构建解析器的方式。
答案 3 :(得分:0)
您需要一个与此类似的规则(假设您使用制表符进行缩进):
\ t:{return TABDENT; }
坦率地说,我始终发现大括号(或开头/结尾)更易于编写和阅读,无论是作为人类还是作为词法分析器/解析器编写者。