Я не вижу трудности в том, чтобы избегать использования глобальных переменных, я лишь использую их для сигнализации об ошибках или подобных вещах.
Подумайте о парсере, что он должен производить?Абстрактное синтаксическое дерево ..
Как это сделано?Это n-арное дерево, в котором каждый узел содержит некоторую информацию и только его дочерние элементы, поэтому глобальные переменные не нужны.
Я покажу вам язык, который я пишу, просто чтобы дать вамидея:
bexp:
bexp T_PLUS bexp { $$ = new ASTBExp($2,$1,$3); }
| bexp T_MINUS bexp { $$ = new ASTBExp($2,$1,$3); }
| bexp T_TIMES bexp { $$ = new ASTBExp($2,$1,$3); }
| bexp T_DIV bexp { $$ = new ASTBExp($2,$1,$3); }
uexp:
raw_value { $$ = $1; }
| UOP_NOT uexp { $$ = new ASTUExp($1,$2); }
| T_LPAREN bexp T_LPAREN { $$ = $2; }
| var_ref { $$ = new ASTVarRef((ASTIdentifier*)$1); }
| call { $$ = $1; }
Как видите, каждый проанализированный узел создается с потомком грамматики, которые также являются семантически потомками абстрактного синтаксического дерева и возвращаются в $$
Корневой элемент - это что-то вроде
start: root { Compiler::instance()->setAST((ASTRoot*)$1); }
;
root:
function_list { $$ = new ASTRoot($1); }
;
, в котором я просто получаю все дерево и передаю его экземпляру моего Compiler
класса.
Теперь, если вы посмотрите на функцию, котораязвонки yyparse()
bool parseSource()
{
//yydebug = 1;
freopen(fileName, "r", stdin);
yyparse();
return !failed;
}
Я просто открываю файл и вызываю процедуру разбора.Эта функция вызывается классом Compiler
здесь:
bool compile()
{
if (!parseSource())
return false;
if (!populateFunctionsTable())
return false;
ast->recursivePrint(0);
Utils::switchStdout(binaryFile);
ast->generateASM();
Utils::revertStdout();
assemble();
return true;
}
Как вы можете видеть, здесь вызывается процедура синтаксического анализа, которая создает целое дерево и затем устанавливает его внутри класса Compiler
.Рекурсивное посещение дерева (функция generateASM
) делает грязную работу.
Надеюсь, это немного прояснит, как вы должны использовать свой синтаксический анализатор, дайте мне знать, если вам нужна дополнительная информация.не нужно делать всю работу в парсере.Просто выполните там синтаксический анализ, все остальное можно решить с помощью рекурсивных вызовов через ваше абстрактное синтаксическое дерево.
Другой практический пример - оператор if / else, о котором вы говорите, в грамматике он определяется как
if_stat:
KW_IF T_LPAREN exp T_RPAREN block %prec LOWER_THAN_ELSE { $$ = new ASTIfStat($3, $5); }
| KW_IF T_LPAREN exp T_RPAREN block KW_ELSE block { $$ = new ASTIfStat($3, $5, $7); }
;
Создается специальный узел для управления конструкцией if / else, который затем работает просто, имея эту generateASM
функцию:
void generateASM()
{
if (m_fbody == NULL)
{
m_condition->generateASM();
printf("NOT\n");
printf("JUMPC iflabel%u\n", labelCounter);
m_tbody->generateASM();
printf("iflabel%u:\r\n", labelCounter);
++labelCounter;
}
else
{
u32 c = labelCounter++;
u32 d = labelCounter++;
m_condition->generateASM();
printf("JUMPC iflabel%u\n", c);
m_fbody->generateASM();
printf("JUMP iflabel%u\n", d);
printf("iflabel%u:\n", c);
m_tbody->generateASM();
printf("iflabel%u:\n", d);
}
}