flex bison 整合到Notepad2中

想用flex bison给Scintilla添加一个控制台, 控制台中可以输入命令, 为了扩展性考虑, 采用flex加bison来实现. 考虑具体的语法格式之前先将flex bison整合进去, 建立一个基本的框架. 下面是碰到的几个问题.

unistd.h的问题

flex产生的lexer如果只是作为C文件编译没有问题, 但是添加到C++的项目中并且是在Windows平台下面的话, 会出现找不到unistd.h的问题, 因为Windows平台根本没有这个文件, 这是unix平台的头文件, 虽然我使用的是gnu for windows, 但是显然这个版本没有修正这个问题.

在产生的c文件中可以看到

 
 
#ifdef __cplusplus
 
#include <stdlib.h>
#include <unistd.h>
 

解决的办法是手动新建D:\Microsoft Visual Studio 8\VC\include\unistd.h文件, 内容为

 
 
#ifndef UNISTD_H
#define UNISTD_H
#include <io.h>
#include <process.h>
#endif 
 
 

yylex identifier not found

yyparse编译为C++似乎没有问题, 但是yylex一定要是以C来编译才行, 否则找不到标识符. 出现这个问题的原因在于bison产生的parser调用yylex是将其当成C函数的, 但是实际上yylex是以C++方式被编译的, C++会对函数名字进行mangling, 其命名方式和以C编译是很不一样的, 因此找不到.

解决的办法是在lex文件头部加入

 
 
#define YY_DECL extern "C" int yylex()
 
 

在yy文件中加入

 
extern "C" int yylex();
 

这样无论是编译还是链接都采用C命名方式, 错误解决.

MSVCRT.lib LIBCMT.lib冲突

添加了lexer和parser的文件再编译出现此问题, 在链接器命令行添加/NODEFULATLIB:msvcrt.lib即可解决.

下面是lexer文件内容:

 
%option noyywrap
 
%{
 
#include "notepad2cmd.tab.hxx"
 
 
#define isatty _isatty
#define YY_DECL extern "C" int yylex()
 
#undef YY_INPUT
#define YY_INPUT(b,r,s) readInputForLexer(b,&r,s)
extern "C" void yyerror(char *s ,...);
extern int readInputForLexer( char *buffer, int *numBytesRead, int maxBytesToRead );
%}
 
%%
[eE][xX][iI][tT] { /*printf("find exit command."); */ return EXIT;}
\n {return EOL;}
[ \t] {}
. {}
%%
 
 
void yyerror(char *s ,...) {
 
}
 

y文件内容

 
%{
#define YYPARSE_PARAM console
#define YYCONSOLE ((sci::ConsoleWin *)console)
 
#include "Console.h"
extern int readInputForLexer( char *buffer, int *numBytesRead, int maxBytesToRead );
extern "C" int yylex();
extern "C" void yyerror(char *s ,...);
%}
 
%token EXIT
%token EOL
%token ECHO
 
 
%%
cmd: EXIT 
     {
         sci::ConsoleWin * con = YYCONSOLE;
         con->Output("haha");
     } 
     EOL 
 | EOL
 ;
%%
 

在需要使用yyparser的地方如下声明:

 
extern int yyparse( void * console );
 

这里yyparse是用C++方式编译的.

向yylex提供输入流的代码如下:

 
static int g_inputOffset;
char * g_Input;
int readInputForLexer( char *buffer, int *numBytesRead, int maxBytesToRead ) {
    int numBytesToRead = maxBytesToRead;
    int bytesRemaining = strlen(g_Input)-g_inputOffset;
    int i;
    if ( numBytesToRead > bytesRemaining ) { numBytesToRead = bytesRemaining; }
    for ( i = 0; i < numBytesToRead; i++ ) {
        buffer[i] = g_Input[g_inputOffset+i];
    }
    *numBytesRead = numBytesToRead;
    g_inputOffset += numBytesToRead;
    return 0;
 
}
 

每次读取到输入命令, 将g_Input指向该字符串, g_inputOffset置为0, 然后调用yyparse即可.

 
    g_Input = CopyRange ( start , end );
    g_inputOffset = 0;
        yyparse(this);
 

执行效果如下: