【问题标题】:Java System.in, newline characters and parsing the command lineJava System.in、换行符和解析命令行
【发布时间】:2019-02-25 19:19:40
【问题描述】:

我正在尝试使用 JFlex 和 Jacc 在 Java 中创建一个简单的解析器。为了测试,我编写了一个简单的词法分析器组合来识别字符串和数字。我设法连接了词法分析器和解析器,但无法处理从 System.io 发送的换行符(ASCII 10)。

这里是 lexer.flex

import java.io.*;

%%

%class Lexer
%implements ParserTokens

%function yylex
%int

%{

    private int token;
    private String semantic;

    public int getToken()
    {
        return token;
    }

    public String getSemantic()
    {
        return semantic;
    }

    public int nextToken()
    {
        try
        {
            token = yylex();
        }
        catch (java.io.IOException e)
        {
            System.out.println("IO exception occured:\n" + e);
        }
        return token;
    }

%}


ID = [a-zA-Z_][a-zA-Z_0-9]*
NUMBER = [0-9]+
SPACE = [ \t]
NL = [\n] | [\r] | [\n\r]


%%

{ID}        { semantic = yytext(); return ID; }
{NUMBER}    { semantic = yytext(); return NUM; }
{SPACE}     {  }
{NL}        { System.out.println("Kill the bugger!"); }
<<EOF>>     {  }

Parser.jacc:

%{

    import java.io.*;

%}

%class Parser
%interface ParserTokens

%semantic String

%token <String> ID
%token <String> NUM
%token <String> SPACE

%type <String> inp


%%

inp : inp sim { System.out.println($2); }
    | sim { System.out.println($1); }
    ;

sim : ID
    | NUM
    ;


%%

    private Lexer lexer;

    public Parser(Reader reader)
    {
        lexer = new Lexer(reader);
    }


    public void yyerror(String error)
    {
        System.err.println("Error: " + error);
    }

    public static void main(String args[]) throws IOException
    {
        Parser parser = new Parser(
            new InputStreamReader(System.in));

        parser.lexer.nextToken();
        parser.parse();
    }

一个示例终端会话:

[johnny@test jacc]$ java Parser
a b c
a
b
Kill the bugger!
1 2 3 4
c
1
2
3
Kill the bugger!

所以当我输入“a b c”时,解析器会打印“a”、“b”,然后是可怜的 ASCII 10。接下来我输入“1 2 3 4”,然后解析器才会打印“c”等。我在Linux / Java 9。

【问题讨论】:

  • 希望在输入中遇到换行符时会发生什么?
  • 我有点希望在 before 换行符 before 换行符之前输入的所有标记。

标签: java io newline yacc jflex


【解决方案1】:

所以当我输入“a b c”时,解析器会打印“a”、“b”,然后 可怜的 ASCII 10。接下来我输入“1 2 3 4”,然后才输入解析器 打印“c”等。我在 Linux / Java 9 上。

这是意料之中的。您的解析器仅打印语义值sim 符号,并且仅在将它们简化为inp 时。尽管在您的特定解析器中,当队列末尾的符号是sim 时,选择总是减少,但如果没有前瞻令牌,它将不会执行这样的缩减。但是,您的 lexer 在获取此类先行标记的过程中扫描换行符后立即打印换行符消息,然后再进行导致打印先前语义值的归约。

如果换行符对您的语法很重要,那么您的词法分析器应该为它们发出标记,而不是直接对它们进行操作,并且您的语法应该考虑这些标记。例如:

inp : line         { System.out.print($1); }
    | inp NL line  { System.out.println("NEWLINE WAS HERE"); System.out.print($3); }
    ;

line : /* empty */ { $$ = new StringBuilder(); }
    | line sim     { $$ = $1.append($2).append('\n'); }
    ;

sim : ID
    | NUM
    ;

假设词法分析器发出一个NL 标记而不是打印一条消息。请注意,该示例中的所有打印都发生在同一级别。如果打印是您真正想做的事情,那么在一个层次上进行所有操作会使控制和预测打印顺序要容易得多

注意:该解析器有点快和脏,包含移位/减少冲突。移位的默认分辨率在那里是正确的。除非您使词法分析器在输入的末尾插入合成 NL 标记,否则要正确解决冲突是很棘手的。此外,您当然需要为line 符号设置正确的令牌类型。

另一方面,如果换行符对语法不重要,那么你应该完全忽略它们。在这种情况下,您的问题根本不会出现。

【讨论】:

  • 感谢详细的回答。是的,新线路很重要。
猜你喜欢
  • 2012-05-02
  • 2012-07-27
  • 2016-03-30
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
相关资源
最近更新 更多