在Flex中使用多个缓冲区时,如何避免在缓冲区之间分割令牌

时间:2017-06-05 04:33:48

标签: c bison flex-lexer

假设我有一个简单的正整数语法和用逗号分隔的字母字符串。我想使用Flex和Bison解析这个语法,并且我想使用Flex的多个输入缓冲区,无论出于何种原因(可能是数据通过网络或串行线或其他任何方式到达)。我看到的问题是,当一个字符串或一个整数(都是可变长度标记)在一个缓冲区的结尾和下一个缓冲区的开头之间被分割时,词法分析器会报告两个标记,而应该只有一个标记。 / p>

在下面的示例中,这些块是asdf g,INT(10)。如果这一切都在一个缓冲区中,则会生成令牌COMMA STR(asdfg) COMMA INT(10)。但是,将'g'与'asdf'放在不同的缓冲区中,词法分析器实际上会产生COMMA STR(asdf) STR(g) COMMA read_more_input: Setting up buffer containing: 10, --accepting rule at line 48 ("10") Starting parse Entering state 0 Reading a token: Next token is token INT_TERM () Shifting token INT_TERM () Entering state 1 Return for a new token: --accepting rule at line 50 (",") Reading a token: Next token is token COMMA () Shifting token COMMA () Entering state 4 Reducing stack by rule 2 (line 67): $1 = token INT_TERM () $2 = token COMMA () -> $$ = nterm int_non_term () Stack now 0 Entering state 3 Return for a new token: --(end of buffer or a NUL) --EOF (start condition 0) read_more_input: Setting up buffer containing: asdf --(end of buffer or a NUL) --accepting rule at line 49 ("asdf") Reading a token: Next token is token STR_TERM () Shifting token STR_TERM () Entering state 6 Return for a new token: --(end of buffer or a NUL) --EOF (start condition 0) read_more_input: Setting up buffer containing: g, --accepting rule at line 49 ("g") Reading a token: Next token is token STR_TERM () syntax errorError: popping token STR_TERM () Stack now 0 3 Error: popping nterm int_non_term () Stack now 0 Cleanup: discarding lookahead token STR_TERM () Stack now 0 。到达缓冲区末尾的逻辑似乎是(1)检查输入是否与令牌匹配,(2)重新填充缓冲区。我认为应该是相反的方式:(2)重新填充缓冲区,(1)检查输入是否与令牌匹配。

我想确保我不会对我改变缓冲区的方式做些蠢事。

标准输出/标准错误:

%{
#include <stdbool.h>
#include "yacc.h"
bool read_more_input(yyscan_t scanner);
%}

%option reentrant bison-bridge

%%

[0-9]+     { yylval->int_value = atoi(yytext); return INT_TERM; }
[a-zA-Z]+  { yylval->str_value = strdup(yytext); return STR_TERM; }
,          { return COMMA;    }
<<EOF>>    {
             if (!read_more_input(yyscanner)) {
                yyterminate();
             }
           }

Lex档案:

%{
// This appears to be a bug. This typedef breaks a dependency cycle between the headers.
// See https://stackoverflow.com/questions/44103798/cyclic-dependency-in-reentrant-flex-bison-headers-with-union-yystype
typedef void * yyscan_t;  

#include <stdbool.h>
#include "yacc.h"
#include "lex.h"
%}

%define api.pure full
%lex-param {yyscan_t scanner}
%parse-param {yyscan_t scanner}
%define api.push-pull push

%union {
  int int_value;
  char * str_value; 
}

%token <int_value> INT_TERM
%type  <int_value> int_non_term
%token <str_value> STR_TERM
%type  <str_value> str_non_term
%token COMMA

%%

complete : int_non_term str_non_term { printf(" === %d === %s === \n", $1, $2); }

int_non_term : INT_TERM COMMA { $$ = $1; }
str_non_term : STR_TERM COMMA { $$ = $1; }

%%

char * packets[]= {"10,", "asdf", "g,"};
int current_packet = 0;

bool read_more_input(yyscan_t scanner) {
  if (current_packet >= 3) {
    fprintf(stderr, "read_more_input: No more input\n");
    return false;
  }

  fprintf(stderr, "read_more_input: Setting up buffer containing: %s\n", packets[current_packet]);
  size_t buffer_size = strlen(packets[current_packet]) + 2;
  char * buffer = (char *) calloc(buffer_size, sizeof(char));
  memcpy(buffer, packets[current_packet], buffer_size - 2);

  yy_scan_buffer(buffer, buffer_size, scanner);
  current_packet++;
  return true; 
}

int main(int argc, char** argv) {

  yyscan_t scanner;
  yylex_init(&scanner) ;

  read_more_input(scanner);

  yyset_debug(1, scanner); 
  yydebug = 1;

  int status;
  yypstate *ps = yypstate_new ();

  YYSTYPE pushed_value;

  do {
    status = yypush_parse(ps, yylex(&pushed_value, scanner), &pushed_value, scanner);
  } while(status == YYPUSH_MORE);

  yypstate_delete (ps);
  yylex_destroy (scanner) ;
  return 0;
}

Yacc文件:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class Main {

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            public void run() {
                new Main().createUI();
            }
        };

        EventQueue.invokeLater(r);
    }

    private void createUI() {

        try {
            JFrame frame = new JFrame();
            frame.setLayout(new BorderLayout());
            JTable table = new JTable();

            String readLine = null;

            StudentTableModel tableModel = new StudentTableModel();
            File file = new File("/home/developer/ffmpeg.txt"/*Give your File Path here*/);

            FileReader reader = new FileReader(file);
            BufferedReader bufReader = new BufferedReader(reader);

            List<Line> studentList = new ArrayList<Line>();
            while((readLine = bufReader.readLine()) != null) {
                String[] splitData = readLine.split(";");

                Line line = new Line();
                line.setName(splitData[0]);
                studentList.add(line);
            }

            tableModel.setList(studentList);
            table.setModel(tableModel);

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new JScrollPane(table));
            frame.setTitle("File to JTable");
            frame.pack();
            frame.setVisible(true);

        } catch(IOException ex) {}
    }

    class Line {

        private String name;
        private String number;

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

    class StudentTableModel extends AbstractTableModel {

        private List<Line> list = new ArrayList<Line>();
        private String[] columnNames = {"column1", "column2"};

        public void setList(List<Line> list) {
            this.list = list;
            fireTableDataChanged();
        }

        @Override
        public String getColumnName(int column) {
            return columnNames[column];
        }

        public int getRowCount() {
            return list.size();
        }

        public int getColumnCount() {
            return columnNames.length;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            switch (columnIndex) {
                case 0:
                    return list.get(rowIndex).getName();
                default:
                    return null;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

这不是多个缓冲区的预期用例。多个输入缓冲区通常用于处理#include甚至宏扩展等内容,其中包含的文本肯定应该遵循标记边界。 (考虑一个#included文件,它有一个未终止的评论......)

如果要以允许令牌流过缓冲区边界的方式将来自不同源的输入粘贴在一起,请重新定义YY_INPUT宏以满足您的需求。

YY_INPUT是用于自定义输入的宏钩子;它被赋予一个缓冲区和一个最大长度,并且它必须将指定的字节数(或更少)复制到缓冲区中,并且还指示提供了许多字节(0字节被视为输入的结尾,此时{{ 1}}将被调用。)

yywrapYY_INPUT内扩展,因此它可以访问yylex个参数,其中包括词法分析器状态。调用lexer中的yylex以扫描程序状态作为参数调用。因此,如果您愿意,可以将这两种机制结合使用。

不幸的是,这不允许&#34;零拷贝&#34;缓冲切换。但是flex并没有针对内存输入缓冲区进行优化:您可以使用yywrap为flex提供缓冲区,但缓冲区必须以两个NUL字节终止,并且在扫描期间会被修改,因此该功能很少有用。

这是一个简单的例子,它允许你使用一个以NULL结尾的类似argv的字符串数组来设置yyscan_buffer,然后将它们全部作为单个输入。 (如果你选择使用argv + 1初始化这个数组,你会注意到它从连续的参数中一起运行标记。)

yylex