Lex 和 Lex 是如何工作的? Yacc 解析器输出值?

发布于 2025-01-07 21:24:46 字数 2052 浏览 1 评论 0原文

因此,对于我正在开发的一个项目,我使用 Lex 和 Yacc 来解析 FTP 配置文件。配置文件看起来像这样:

global {
    num_daemons = 10
    etc = /etc/ftpd
};

host "ftp-1.foobar.com" {
    ftproot = /var/ftp/server1
    max_out_bandwidth = 20.7
};

host "ftp-2.foobar.com" {
    ftproot = /var/ftp/server2
    exclude = /var/ftp/server2/private
};

host "ftp-3.foobar.com" {
    ftproot = /var/ftp/server3
};

现在,我的问题是,如何以可用的方式获取这些信息?假设我想将主机令牌后面的地址之类的内容放入结构中。我该怎么做呢?另外,我如何简单地将解析到命令行的值打印出来?另外,要运行它,我是否只需在编译的 C 程序中捕获配置文件和管道?预先感谢您的任何帮助!

这是我的代码:

%{
// tokens.l
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"

int yyparse();

%}

%option noyywrap

%x OPTION
%x OPTID

%%

<INITIAL>global             { return GLOBAL; }
<INITIAL>host               { return HOST; }
<INITIAL>"[a-zA-z1-9./-]+"  { return NAME; }
<INITIAL>\{                 { return CURLY_OPEN; BEGIN OPTION; }
<INITIAL>\n                 { return EOLN; }
<INITIAL><<EOF>>            { return EOFTOK; }


<OPTION>[a-zA-z1-9./-_]+    { return ID_NAME; BEGIN OPTID; }
<OPTION>[\t]                {}
<OPTION>[\};]               { return OPTION_CLOSE; BEGIN INITIAL;}

<OPTID>[a-zA-z1-9./-]+      { return ID_STRING; BEGIN OPTION; }
<OPTID>[0-9.]+              { return ID_NUM; BEGIN OPTION; }
<OPTID>[\n]                 { return EOLN; }

%%

int main(int argc, char **argv) {
    // Where I am confused..
}

和我的 yacc 文件:

%{
// parse.y
#include <stdio.h>
#include <stdlib.h>

int yyerror(char *);
int yylex(void);

%}

%token ERROR EOLN EOFTOK
%token OPTION_CLOSE GLOBAL HOST NAME ID_NAME ID_STRING ID_NUM CURLY_OPEN

%%

input
    : lines EOFTOK  { YYACCEPT; }
    ;
lines
    :
    | lines line
    ;
line
    : option
    | opident
    | OPTION_CLOSE
    ;
option
    : GLOBAL CURLY_OPEN
    | HOST NAME CURLY_OPEN
    ;
opident
    : ID_NAME '=' ID_STRING
    | ID_NAME '=' ID_NUM
    ;
%%

int yyerror(char *msg) {}

So for a project that I'm working on, I am using Lex and Yacc to parse a FTP configuration file. The configuration files look something like this:

global {
    num_daemons = 10
    etc = /etc/ftpd
};

host "ftp-1.foobar.com" {
    ftproot = /var/ftp/server1
    max_out_bandwidth = 20.7
};

host "ftp-2.foobar.com" {
    ftproot = /var/ftp/server2
    exclude = /var/ftp/server2/private
};

host "ftp-3.foobar.com" {
    ftproot = /var/ftp/server3
};

Now, my question is, how do I obtain this information in a usable way? Let's say I wanted to put things like the address after the host token into a struct. How would I do that? Also, how would I simply print out the values that I've parsed to the command line? Also, to run it, do I just cat the config file and pipe in the compiled c program? Thanks in advance for any help!

Here is my code:

%{
// tokens.l
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"

int yyparse();

%}

%option noyywrap

%x OPTION
%x OPTID

%%

<INITIAL>global             { return GLOBAL; }
<INITIAL>host               { return HOST; }
<INITIAL>"[a-zA-z1-9./-]+"  { return NAME; }
<INITIAL>\{                 { return CURLY_OPEN; BEGIN OPTION; }
<INITIAL>\n                 { return EOLN; }
<INITIAL><<EOF>>            { return EOFTOK; }


<OPTION>[a-zA-z1-9./-_]+    { return ID_NAME; BEGIN OPTID; }
<OPTION>[\t]                {}
<OPTION>[\};]               { return OPTION_CLOSE; BEGIN INITIAL;}

<OPTID>[a-zA-z1-9./-]+      { return ID_STRING; BEGIN OPTION; }
<OPTID>[0-9.]+              { return ID_NUM; BEGIN OPTION; }
<OPTID>[\n]                 { return EOLN; }

%%

int main(int argc, char **argv) {
    // Where I am confused..
}

and my yacc file:

%{
// parse.y
#include <stdio.h>
#include <stdlib.h>

int yyerror(char *);
int yylex(void);

%}

%token ERROR EOLN EOFTOK
%token OPTION_CLOSE GLOBAL HOST NAME ID_NAME ID_STRING ID_NUM CURLY_OPEN

%%

input
    : lines EOFTOK  { YYACCEPT; }
    ;
lines
    :
    | lines line
    ;
line
    : option
    | opident
    | OPTION_CLOSE
    ;
option
    : GLOBAL CURLY_OPEN
    | HOST NAME CURLY_OPEN
    ;
opident
    : ID_NAME '=' ID_STRING
    | ID_NAME '=' ID_NUM
    ;
%%

int yyerror(char *msg) {}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

假情假意假温柔 2025-01-14 21:24:46

在调用解析器之前,您通常会拥有可访问和设置的变量,例如键/值对的链接列表:

typedef struct sNode {
    char *key;
    char *val;
    struct sNode *next;
} tNode;
tNode *lookupHead = NULL;

然后,在您的 Yacc 代码中,类似于:

opident
    : ID_NAME '=' ID_STRING { addLookupStr (lookupHead, $1, $3); }
    | ID_NAME '=' ID_NUM    { other function call here }
    ;

这基本上会在找到规则时执行该代码(替换规则中项目的 $ 变量,$1ID_NAME 标记的值,$2 是 <代码>=,依此类推)。

该函数类似于:

void addLookupStr (char *newkey, char *newval) {
    // Check for duplicate keys, then attempt to add. All premature returns
    // should also be logging errors and setting error flags as needed.

    tNode *curr = lookupHead;
    while (curr != NULL) {
        if (strcmp (curr->key, newkey) == 0)
            return;
        curr = curr->next;
    }

    if ((curr = malloc (sizeof (tNode))) == NULL)
        return;

    if ((curr->key = strdup (newkey)) == NULL) {
        free (curr);
        return;
    }

    if ((curr->val = strdup (newval)) == NULL) {
        free (curr->newkey);
        free (curr);
        return;
    }

    // All possibly-failing ops complete, insert at head of list.

    curr->next = lookupHead;
    lookupHead = curr;
}

You would generally have variables which were accessible and set up before calling the parser, like a linked list of key/value pairs:

typedef struct sNode {
    char *key;
    char *val;
    struct sNode *next;
} tNode;
tNode *lookupHead = NULL;

Then, in your Yacc code, something like:

opident
    : ID_NAME '=' ID_STRING { addLookupStr (lookupHead, $1, $3); }
    | ID_NAME '=' ID_NUM    { other function call here }
    ;

This would basically execute that code as the rules are found (replacing the $ variables with the item in the rule, $1 is the value for the ID_NAME token, $2 is the =, and so on).

The function would be something like:

void addLookupStr (char *newkey, char *newval) {
    // Check for duplicate keys, then attempt to add. All premature returns
    // should also be logging errors and setting error flags as needed.

    tNode *curr = lookupHead;
    while (curr != NULL) {
        if (strcmp (curr->key, newkey) == 0)
            return;
        curr = curr->next;
    }

    if ((curr = malloc (sizeof (tNode))) == NULL)
        return;

    if ((curr->key = strdup (newkey)) == NULL) {
        free (curr);
        return;
    }

    if ((curr->val = strdup (newval)) == NULL) {
        free (curr->newkey);
        free (curr);
        return;
    }

    // All possibly-failing ops complete, insert at head of list.

    curr->next = lookupHead;
    lookupHead = curr;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文