在flex,bison,c ++中实现Wolfram语言

Implementing Wolfram language in flex, bison, c++

本文关键字:实现 Wolfram 语言 flex bison      更新时间:2023-10-16

在看到数学和symja等项目后,我正在尝试使用C++中的flex和bison为Wolfram语言实现开源解析器。调用 bison -d 和 flex++ 不会引发任何问题,但是当我使用 g++ 时,我收到以下错误消息:

parser.tab.cpp:1242:16: error: use of undeclared identifier 'yylex'
  yychar = YYLEX;
           ^
parser.tab.cpp:598:16: note: expanded from macro 'YYLEX'
# define YYLEX yylex ()
           ^
1 error generated.

这是我的 .lpp 和 .ypp 文件供参考

lexer.lpp

%{
#include <iostream>
#include "parser.tab.hpp"
using namespace std;
extern "C"
{
    int yylex(void);
}
%}
%option c++
%option noyywrap
%%
[1-9][0-9]*(.[0-9]*)?     { return NUM; }
"["        { return LBRACE; }
"]"        cout << "rBrace" << endl;
"("        cout << "lParen" << endl;
")"        cout << "rParen" << endl;
"{"        cout << "lBracket" << endl;
"}"        cout << "rBracket" << endl;
","         cout << "comma" << endl;
"@@"        cout << "apply" << endl;
"Apply["   cout << "apply" << endl;
"/@"        cout << "map" << endl;
"Map["     cout << "map" << endl;
"/."        cout << "rule" << endl;
"==="       cout << "sameQ" << endl;
"SameQ["   cout << "sameQ" << endl;
"+"         cout << "plus" << endl;
"-"         cout << "minus" << endl;
"*"         cout << "times" << endl;
"/"         cout << "divide" << endl;
"^"         cout << "power" << endl;
"Power["   cout << "power" << endl;
--Abbreviated--
.           ECHO;
%%
int main()
{
    FlexLexer* lexer = new yyFlexLexer;
    while(lexer->yylex() != 0)
    ;
    return 0;
}

解析器.ypp

%{
#include <iostream>
#include <string>
using namespace std;
extern "C"
{
    int yyparse(void);
}
void yyerror(const char *s);
%}
%union {
    double dval;
    char *str;
}
%token <dval> NUM;
%token <str> RBRACE;
%token <str> LBRACE;
%%
expr:
    NUM     { cout << $1 << endl;}
    | NUM "+" NUM { cout << $1 + $3}
    | NUM "-" NUM { cout << $1 - $3}
    | NUM "*" NUM { cout << $1 * $3}
    | NUM "/" NUM { cout << $1 / $3}
    ;
%%
int main(int argc, char **argv)
{
    yyparse();
}
void yyerror(const char *s)
{
    cout << s << endl;
}

任何帮助将不胜感激。谢谢!

yylex

生成的扫描程序中定义,并在生成的解析器中(自动)使用。由于结果只是普通的 C(++),因此没有魔法;如果在文件中使用 yylex,则需要在该文件中声明它。

您可能希望 bison 自动包含声明,但事实并非如此。首先,它不知道您想(不必要且可能徒劳地)将声明包装在extern "C" {...}中。


此外,您将遇到C++接口的问题。 yylex 是 flex C++ API 中的一个成员函数,因此您不能将其声明为 extern "C" ,也不能只在外部文件中将其调用为yylex

YMMV,但我个人更喜欢使用普通(稳定且文档齐全)的 C API,它可以很好地编译C++,避免任何extern "C"声明。

如果要避免全局变量,请使用可重入扫描程序/纯解析器接口。

最后,flex附带了一个非常好的调试选项,只需在命令行上指定-d,就可以以几乎零成本使用。使用该标志生成的扫描程序将自动输出有关扫描的每个令牌的信息性消息,删除命令行标志比编辑整个 flex 描述要容易得多。

bison 具有类似的机制,但它不是那么自动:您需要在生成解析器时启用它,然后需要通过设置运行时标志来打开它。两者都在各自的手册中有详细的记录。