我們在之前的例子中,主程式在讀取完資料後就結束了。如果要繼續做後續的部分,那就要將讀進來的資料找個地方存起來。因此,在 Yacc 檔案裡定義變數的做法就行不通了。但是,如果我們把變數定義在主程式,那要怎麼讓 yacc 檔案可以使用呢?
在 C++中,若是要讓某個檔案裡的變數可以被其他檔案使用,可以使用 extern
這個方法。我們在這裡就不仔細解說 extern
的用法了,但有一個重點一定要記得:在編譯的時候,定義變數的檔案一定要先編譯,不然引用變數的檔案在編譯時會找不到宣告的源頭,導致編譯失敗。因此,若是我們要將變數定義在 main.cpp 裡面的話,就要先編譯 main.cpp 再編譯 yacc 跟 lex 檔案。
我們直接用下面的範例來說明外部變數在 lex & yacc 運用的情形。
請在 main.cpp 中宣告一個空的 int array ,接著用 parser 讀取檔案中向量,最後在 parser 與主程式中印出該向量。
%{
#include "main.h"
#include "yacc.tab.h"
%}
posint ([0-9]+)
blank_chars ([ \f\r\t\v]+)
%%
{posint} { yylval = atoi(yytext); return NUMBER; }
{blank_chars} { ; }
"[" { return yytext[0]; }
"]" { return yytext[0]; }
\n { ; }
%%
int yywrap(void) {
cout << "Finish parsing!" << endl;
return 1;
}
%{
#include "main.h"
void yyerror(const char *s);
extern int yylex();
extern int yyparse();
extern int* array;
extern int* arraySize;
void PrintArrayYacc() {
cout << "Array in yacc: [ ";
for (int i = 0; i < (*arraySize); i++) {
cout << array[i] << " ";
}
cout << "]\n";
}
%}
%token NUMBER
%%
func:
'[' array ']' { PrintArrayYacc(); }
;
array:
| array NUMBER { array[(*arraySize)] = $2; (*arraySize)++; }
;
%%
void yyerror(const char *s) {
cerr << s << endl;
}
#ifndef MAIN_H
#define MAIN_H
#include <iostream>
#include <stdio.h>
using namespace std;
#define YYSTYPE int
#endif
#include "main.h"
extern int yyparse(void);
extern FILE* yyin;
int *arraySize = nullptr;
int *array = nullptr;
void PrintArrayMain(int *arr, int arrSize) {
cout << "Array in main: [ ";
for (int i = 0; i < arrSize; i++) {
cout << arr[i] << " ";
}
cout << "]\n";
}
int main()
{
const char* sFile = "file.txt";
FILE* fp = fopen(sFile, "r");
if (fp == NULL) {
printf("cannot open %s\n", sFile);
return -1;
}
int arrayMainSize = 0;
int *arrayMain = new int[64];
array = arrayMain;
arraySize = (&arrayMainSize);
yyin = fp;
yyparse();
fclose(fp);
PrintArrayMain(arrayMain, arrayMainSize);
delete [] arrayMain;
return 0;
}
LEX=flex
YACC=bison
CC=g++
OBJECT=main
$(OBJECT): main.o yacc.tab.o lex.yy.o
$(CC) main.o lex.yy.o yacc.tab.o -o $(OBJECT)
@./$(OBJECT)
lex.yy.o: lex.yy.c yacc.tab.h main.h
$(CC) -c lex.yy.c
yacc.tab.o: yacc.tab.c main.h
$(CC) -c yacc.tab.c
yacc.tab.c yacc.tab.h: yacc.y
$(YACC) -d yacc.y
lex.yy.c: lex.l
$(LEX) lex.l
main.o: main.cpp
$(CC) -c main.cpp
clean:
@del -f $(OBJECT) *.o lex.yy.c yacc.tab.h yacc.tab.c main.exe
[ 1 2 3 ]
Array in yacc: [ 1 2 3 ]
Finish parsing!
Array in main: [ 1 2 3 ]
可以看到,我們成功將文本的陣列存到 main function 宣告的陣列中了!
今天的內容比較複雜,但了解之後,便可以把 parser 讀進來的資料繼續用 C++ 達成更多目的了。