Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用可为空/可选标记时进行下一次循环迭代_Java_Javacc - Fatal编程技术网

Java 使用可为空/可选标记时进行下一次循环迭代

Java 使用可为空/可选标记时进行下一次循环迭代,java,javacc,Java,Javacc,我必须编写用于java应用程序的解析器,该应用程序将接受: 数字(例如:1、2、3) 数字范围(例如:1-3) 命名范围(例如:组\u 1\u匹配) 每个令牌由以下任一项分隔: <WHITE : ([" ", "\t"])+ > <COMMA : (",") > <SEMICOLON : (";") > <EOL : ("\r" | "\n" | "\r\n") > 测试字符串如下所示: “12,13-13,14-14-15-15-16-16

我必须编写用于java应用程序的解析器,该应用程序将接受:

  • 数字(例如:1、2、3)
  • 数字范围(例如:1-3)
  • 命名范围(例如:组\u 1\u匹配)
每个令牌由以下任一项分隔:

<WHITE : ([" ", "\t"])+ >
<COMMA : (",") >
<SEMICOLON : (";") >
<EOL : ("\r" | "\n" | "\r\n") >
测试字符串如下所示:
“12,13-13,14-14-15-15-16-16\n17-17\n 18-18\n 19-19\n组1\u A组1\u A组1\u A组1\u A组1\u A组1\u A组1\u A组20”

我已经尝试了几种方法来定义“-”周围的空格,但总的来说,要么在无限嵌套循环中结束,该循环处理给定的简单字符串,直到结束,然后从头开始,要么就是无法进行下一次迭代。如果有一种方法可以在不使用下一个令牌的情况下检查访问下一个令牌,那就很容易了

SKIP: {
    < QUOTATION :  ( ["\""] ) > |
    < APOSTROPHE : ( ["'"] ) >
}

TOKEN: {
    < NAME :            ( ["a"-"z", "A"-"Z"])+ (["a"-"z", "A"-"Z", "_", "0"-"9"] )* > |
    < NUM :             ( ["0"-"9"] ){1,5} > |
    < WHITE :           ( [" ", "\t"] ) > |
    < EOL :             ( "\n" | "\r" | "\r\n" ) > |
    < COMMA :           ( [","] ) > |
    < SEMICOLON :       ( [";"] ) >
}

Map<String, List<String>> parse() : {
    Map<String, List<String>> result = new HashMap<String, List<String>>();
    List<String> single = new ArrayList<String>();
    List<String> range = new ArrayList<String>();
    List<String> named = new ArrayList<String>();
    result.put(SINGLE, single);
    result.put(RANGE, range);
    result.put(NAMED, named);
    Token name = null;
    Token first = null;
    Token last = null;
}
{
    (<WHITE>)*
    (
        (name = <NAME> |
            first = <NUM>
            (LOOKAHEAD(2) (<WHITE>)* "-" (<WHITE>)* last = <NUM>)?
        )
        ((LOOKAHEAD(2) <EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+ | <EOF>)

        {
            if (name != null) {
                named.add(name.image);
            } else if (first != null && last == null) {
                single.add(first.image);
            } else if (first != null && last != null) {
                String s = first.image + " - " + last.image;
                range.add(s);
            } else {
                System.err.println("Parser error found");
            }

            name = null;
            first = null;
            last = null;
        }
    )+
    {
        return result;
    }
}
当解析器不知道空格是来自破折号之前的空格还是来自作为整数分隔符的空格时,就会出现问题


如果您知道如何修改JavaCC以正确解析提供的字符串,我们将不胜感激。

让我们从JavaCC后退一步,看看您的语法到底是什么

parse --> ows ( body )+
body --> part sep
part --> <NAME>
part --> <NUM>
part --> <NUM> ows "-" ows <NUM>
sep --> (<EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+
sep -->  EOF
ows --> (<WHITE>)*

第一种解决方案:语法前瞻

OP表示,如果有一种方法可以在不使用下一个代币的情况下检查下一个代币,那就很容易了。有。这叫做句法先行

我们唯一需要向前看的地方是区分
零件
的第二个和第三个产品。 让我们把它们结合起来

part --> <NAME>
part --> <NUM> ( ows "-" ows <NUM> )?
内联
part
并引入非终结
afternum

parse --> ows body
body --> <NAME> (sep body | <EOF>)
body --> <NUM> afternum
afternum --> ( ows "-" ows <NUM> )? (sep body | <EOF>)
sep --> (<EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+
ows --> (<WHITE>)*
现在问题出在
moreafternum
中,因为如果下一个标记是
WHITE
,则两种选择都是可行的

让我们稍微操纵一下
moreafternum
。目的是公开
WHITE
token,这样我们就可以将它排除在外

    moreafternum
= By definition
    ows "-" ows <NUM> (sep body | EOF) | sep? body
= Expand the ?
      ows "-" ows <NUM> (sep body | EOF)
    | body
    | sep body
= Expand first `ows` and split white from other cases
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | sep body
= Expand the `sep` in the fourth case
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | (WHITE | nonwesep) sep? body
= Split the fourth case
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | WHITE sep? body
    | nonwssep sep? body
= Duplicate the fourth choice
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | WHITE sep? body
    | body
    | WHITE sep? body
    | nonwssep sep?
= Combine the second and third choices.
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body )
    | body
    | WHITE sep? body
    | nonwssep sep? body
= combine the third, fourth, and fifth choices
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body)
    | sep? body
= Definition of moreafternum
      "-" ows <NUM> (sep body | EOF)
    | WHITE moreafternum
    | sep? body
如果我们用JavaCC对这个产品进行编码,那么当下一个标记为白色时,第二个和第三个选择之间仍然存在选择冲突。JavaCC更喜欢第二名而不是第三名,这正是我们想要的。如果你不喜欢这个警告,你可以向前看。请注意,此前瞻不会更改生成的Java代码,它只是抑制警告

void moreafternum() : {} {
       "-" ows() <NUM> (sep() body() | <EOF>)
|
       // LOOKAHEAD( <WHITE> ) // Optional lookahead to suppresss the warning
       <WHITE> moreafternum()
|
       ( sep() )? body() }
总而言之,我们得到了

parse --> ows body 

body --> <NAME> (sep body | <EOF>)
body --> <NUM> afternum

afternum --> "-" ows <NUM> (sep body | <EOF>)
afternum --> <WHITE> moreafternum
afternum --> nonwssep (sep)? body
afternum --> EOF

moreafternum --> "-" ows <NUM> (sep body | EOF)
moreafternum --> <WHITE> moreafternum
moreafternum --> ( nonwssep (sep)? )? body

nonwssep --> <EOL> | <COMMA> | <SEMICOLON>

sep --> (nonwssep | <WHITE>)+

ows --> (<WHITE>)*
parse-->ows正文
正文-->(九月正文|)
正文-->后数
afternum-->“-”ows(sep body |)
afternum-->moreafternum
afternum-->非WSSEP(sep)?身体
afternum-->EOF
moreafternum-->“-”ows(sep body | EOF)
moreafternum-->moreafternum
moreafternum-->(非WSSEP(sep)?)?身体
非WSSEP-->
sep-->(非WSSEP |)+
ows-->()*

这是LL(1),因此您可以将其翻译为JavaCC,而无需提前查看。

这一语法让我感到奇怪的一点是,
EOF
必须紧跟在最后一个
NUM
NAME
之后,中间甚至没有空格或行尾。@TheodoreNorvell:这是因为它是用户输入,而且可能是(但不是必须)由用于输入数据的应用程序转换(Excel)。我只是想能够使用几乎任何由用户提供的任何格式,它将是。。。或多或少我所知道的是,在实际数据之前和之后可能都有空格。它们可以用换行符、空格、逗号或分号分隔。这是用户输入,所以我不知道他们会输入什么。此解析器应分析excel工作簿中特定单元格内的IP端口范围。已经有很多文件,所以我必须导入它们。我指出,例如“123”,根据您的语法是一个解析错误,因为您的语法要求EOF正好位于最后的NUM或NAME之后。这使得将语法转换成LL(1)变得相当困难和复杂。如果您正在编写一种特殊语言来解析一些混乱的输入,我建议您跳过空格和制表符。这使得语法更加简单,而不会有太多的输入被误解的风险。但是,只要有分隔符,这难道不会使eof成为可选的吗?我认为当+|是有意义的,因为在最后一个数字之后可能会有分隔符(有意或无意)和后来的eof或只是eof。您将空白命名为“ows”函数。它是OneWhiteSpace的缩写吗?除此之外,它就像@dimwittedanimal说的:写得很好。但我终于发现了我的错误。这是我用于解析器的InputStream。我使用了CharSequenceInputStream。当我将其更改为StringReader或StringInputStream时,它消失了,解析器在给定定义下按预期工作。尽管如此,还是要感谢您的详细回答。
ows
是“可选空白”。我真的怀疑更改输入流是否能解决您原始语法的所有问题。正如我在上面的答案中指出的,您正在尝试使用固定长度的前瞻性,而这根本不起作用。特别是
(LOOKAHEAD(2)(*“-”)?
前面有两个空格时会出错。改用
LOOKAEAD(()*“-”)
。@dimwittedanimal。谢谢第二部分花了很长时间,我犯了很多错误并改正了很多。如果一个也没有留下,我会感到惊讶的。总的来说,我认为这是一个很好的例子,说明前瞻是一个比LL(1)更清晰、更简单的解决方案。是的。我也是。关于前瞻(2)。我还在学习这门语言,不知道它有多少可能性。谢谢你指出这一点。更改输入流确实解决了我的问题,但我会将您的答案标记为正确,因为其中包含更多内容。apache库中的CharSequenceInputStream中一定有错误,因为在使用它时,解析器进入循环。当它到达终点时,它就从起点开始。仅仅改变输入流就解决了我发帖几个小时后的问题,但即使这样,你的答案也要好得多。T
part --> <NAME>
part --> <NUM> ( LOOKAHEAD( ows "-" ) ows "-" ows <NUM> )?
void parse() : { }
{
    ows() body }
}

void body() : { }
{
    part() ( sep() body()  | <EOF> )
}

void part() : { }
{
   <NAME>
|
   <NUM>
   ( LOOKAHEAD( ows() "-")
     ows() "-" ows() <NUM>
   )?
}

void sep() : {}
{
    (<EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+
}

void ows() : {}
{
    (<WHITE>)*
}
parse --> ows body
body --> part (sep body | <EOF>)
part --> <NAME>
part --> <NUM> ( ows "-" ows <NUM> )?
sep --> (<EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+
ows --> (<WHITE>)*
parse --> ows body
body --> <NAME> (sep body | <EOF>)
body --> <NUM> afternum
afternum --> ( ows "-" ows <NUM> )? (sep body | <EOF>)
sep --> (<EOL> | <COMMA> | <SEMICOLON> | <WHITE>)+
ows --> (<WHITE>)*
afternum --> "-" ows <NUM> (sep body | <EOF>)
afternum --> nonwssep (sep)? body
afternum --> <WHITE> moreafternum
afternum --> EOF

moreafternum --> ows "-" ows <NUM> (sep body | EOF)
               | sep? body

nonwssep --> <EOL> | <COMMA> | <SEMICOLON>
    moreafternum
= By definition
    ows "-" ows <NUM> (sep body | EOF) | sep? body
= Expand the ?
      ows "-" ows <NUM> (sep body | EOF)
    | body
    | sep body
= Expand first `ows` and split white from other cases
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | sep body
= Expand the `sep` in the fourth case
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | (WHITE | nonwesep) sep? body
= Split the fourth case
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | body
    | WHITE sep? body
    | nonwssep sep? body
= Duplicate the fourth choice
      "-" ows <NUM> (sep body | EOF)
    | WHITE ows "-" ows <NUM> (sep body | EOF)
    | WHITE sep? body
    | body
    | WHITE sep? body
    | nonwssep sep?
= Combine the second and third choices.
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body )
    | body
    | WHITE sep? body
    | nonwssep sep? body
= combine the third, fourth, and fifth choices
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body)
    | sep? body
= Definition of moreafternum
      "-" ows <NUM> (sep body | EOF)
    | WHITE moreafternum
    | sep? body
moreafternum --> "-" ows <NUM> (sep body | EOF)
               | <WHITE> moreafternum
               | sep? body
void moreafternum() : {} {
       "-" ows() <NUM> (sep() body() | <EOF>)
|
       // LOOKAHEAD( <WHITE> ) // Optional lookahead to suppresss the warning
       <WHITE> moreafternum()
|
       ( sep() )? body() }
     moreafternum
=  From above
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body)
    | body
    | WHITE sep? body
    | nonwssep sep? body
= Fourth choice is subsumed by the second.
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body)
    | body
    | nonwssep sep? body
= Combine last two choices
      "-" ows <NUM> (sep body | EOF)
    | WHITE ( ows "-" ows <NUM> (sep body | EOF) | sep? body)
    | (nonwssep sep?)? body
= Original definition of moreaftersep
      "-" ows <NUM> (sep body | EOF)
    | WHITE moreaftersep
    | (nonwssep sep?)? body
parse --> ows body 

body --> <NAME> (sep body | <EOF>)
body --> <NUM> afternum

afternum --> "-" ows <NUM> (sep body | <EOF>)
afternum --> <WHITE> moreafternum
afternum --> nonwssep (sep)? body
afternum --> EOF

moreafternum --> "-" ows <NUM> (sep body | EOF)
moreafternum --> <WHITE> moreafternum
moreafternum --> ( nonwssep (sep)? )? body

nonwssep --> <EOL> | <COMMA> | <SEMICOLON>

sep --> (nonwssep | <WHITE>)+

ows --> (<WHITE>)*