【问题标题】:how can I make this JAVACC grammar work with [ ]?我怎样才能使这个 JAVACC 语法与 [] 一起工作?
【发布时间】:2015-05-07 23:08:23
【问题描述】:

我正在尝试更改 JSqlParser 项目中的语法,该项目处理指定标准 SQL 语法的 javacc 语法文件 .jj。我很难让一个部分开始工作,我把它缩小到以下,大大简化了语法。

基本上我有一个定义 Column : [table ] 。字段

但表格本身也可以包含“。” char,这会导致混乱。

我直观地认为以下语法应该接受以下所有句子:

选择 mytable.myfield

选择我的字段

选择 mydb.mytable.myfield

但实际上它只接受上面的第 2 和第 3。每当它看到“.”时,它就会进入要求 2 点版本的表格(即表格的第一个推导规则)

我怎样才能使这个语法起作用?

非常感谢 杨

    options{
        IGNORE_CASE=true ;
        STATIC=false;
            DEBUG_PARSER=true;
        DEBUG_LOOKAHEAD=true;
        DEBUG_TOKEN_MANAGER=false;
    //  FORCE_LA_CHECK=true;
        UNICODE_INPUT=true;
    }

    PARSER_BEGIN(TT)

    import java.util.*;

    public class TT {

    }
    PARSER_END(TT)


    ///////////////////////////////////////////// main stuff concerned
    void Statement() :
    { }
    {
    <K_SELECT> Column()
    }

    void Column():
    {
    }
    {
    [LOOKAHEAD(3) Table()  "." ]
    //[ 
    //LOOKAHEAD(2) (
    //      LOOKAHEAD(5) <S_IDENTIFIER> "."  <S_IDENTIFIER>  
    //      |
    //      LOOKAHEAD(3) <S_IDENTIFIER>
    //)
    //
    //
    //
    //]

    Field()
    }

    void Field():
    {}{
       <S_IDENTIFIER>
    }

    void Table():
    {}{
            LOOKAHEAD(5) <S_IDENTIFIER> "."  <S_IDENTIFIER>
            |
            LOOKAHEAD(3) <S_IDENTIFIER>
    }

    ////////////////////////////////////////////////////////



SKIP:
{
    " "
|   "\t"
|   "\r"
|   "\n"
}

TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
{
<K_CREATE: "CREATE">
|
<K_SELECT: "SELECT">
}


TOKEN : /* Numeric Constants */
{
   < S_DOUBLE: ((<S_LONG>)? "." <S_LONG> ( ["e","E"] (["+", "-"])? <S_LONG>)?
                        |
                        <S_LONG> "." (["e","E"] (["+", "-"])? <S_LONG>)?
                        |
                        <S_LONG> ["e","E"] (["+", "-"])? <S_LONG>
                        )>
  |     < S_LONG: ( <DIGIT> )+ >
  |     < #DIGIT: ["0" - "9"] >
}


TOKEN:
{
        < S_IDENTIFIER: ( <LETTER> | <ADDITIONAL_LETTERS> )+ ( <DIGIT> | <LETTER> | <ADDITIONAL_LETTERS> | <SPECIAL_CHARS>)* >
|       < #LETTER: ["a"-"z", "A"-"Z", "_", "$"] >
|   < #SPECIAL_CHARS: "$" | "_" | "#" | "@">
|   < S_CHAR_LITERAL: "'" (~["'"])* "'" ("'" (~["'"])* "'")*>
|   < S_QUOTED_IDENTIFIER: "\"" (~["\n","\r","\""])+ "\"" | ("`" (~["\n","\r","`"])+ "`") | ( "[" ~["0"-"9","]"] (~["\n","\r","]"])* "]" ) >

/*
To deal with database names (columns, tables) using not only latin base characters, one
can expand the following rule to accept additional letters. Here is the addition of german umlauts.

There seems to be no way to recognize letters by an external function to allow
a configurable addition. One must rebuild JSqlParser with this new "Letterset".
*/
|   < #ADDITIONAL_LETTERS: ["ä","ö","ü","Ä","Ö","Ü","ß"] >
}

【问题讨论】:

    标签: java parsing javacc jsqlparser


    【解决方案1】:

    你可以这样重写你的语法

    Statement --> "select" Column
    Column --> Prefix <ID>
    Prefix --> (<ID> ".")*
    

    现在唯一的选择是是否迭代。假设一个“。”不能跟随一个列,这很容易通过前瞻 2 来完成:

    Statement --> "select" Column
    Column --> Prefix <ID>
    Prefix --> (LOOKAHEAD( <ID> ".") <ID> ".")*
    

    【讨论】:

    • 感谢西奥多,但它似乎不起作用。看我下面的答案(我不得不打开一个新的答案,因为评论不允许粘贴代码)
    • Theodore:将 LOOKAHEAD() 值更改为数字后,它可以工作。非常感谢
    • 但我必须将 Prefix() 规则分离为 Prefix --> Table() "。" | {} 因为在实际用例中,我最初的目标是将 Table() 定义隔离到一个地方。当我这样做时,解析器不起作用。
    • 我怀疑最近失败的原因是Javacc的lookahead()只看生产本身的长度。在这种情况下,除非您查看生产本身的所有规则之外,否则无法确定生产(即在这种情况下,您需要查找 EOF)
    • 它仅适用于数字前瞻的原因是您没有使用与我的答案相同的前瞻。
    【解决方案2】:

    flex+bison(LR 解析器)中的以下语法确实可以正常工作,正确识别以下所有句子:

    创建 mydb.mytable 创建我的表 选择 mydb.mytable.myfield 选择 mytable.myfield 选择我的领域

    确实是LL解析器的限制

    %%
    
    statement:
            create_sentence
            |
            select_sentence
            ;
    
    create_sentence:  CREATE table
            ;
    
    select_sentence: SELECT  table '.'  ID
                    |
                    SELECT ID
                    ;
    
    table : table '.' ID
            |
            ID
            ;
    
    
    
    %%
    

    【讨论】:

      【解决方案3】:

      如果您需要 Table 成为它自己的非终结符,您可以通过使用一个布尔参数来执行此操作,该参数说明该表是否应该跟一个点。

      void Statement():{}{
          "select" Column() | "create" "table" Table(false) }
      
      void Column():{}{
          [LOOKAHEAD(<ID> ".") Table(true) "."] <ID> }
      
      void Table(boolean expectDot):{}{
          <ID> MoreTable(expectDot) }
      
      void MoreTable(boolean expectDot) {
          LOOKAHEAD("." <ID> ".", {expectDot}) "." <ID> MoreTable(expectDot)
      |
          LOOKAHEAD(".", {!expectDot}) "." <ID> MoreTable(expectDot)
      |
          {}
      }
      

      这样做会直接或间接地排除在任何语法前瞻规范中使用 Table 的可能性。例如。您不应该在语法中的任何地方使用LOOKAHEAD( Table()),因为在语法超前期间不使用语义超前。有关详细信息,请参阅常见问题解答。

      【讨论】:

      • 谢谢,这很有趣,第一次看到这种语法。我是。好奇这如何转化为 LL 解析序列?在 bison 中你可以查看生成的状态表,但是使用 javacc 唯一可用的诊断是 java 代码,这很神秘
      • 有趣的是,在 LR 解析器的情况下,只要解析器看到 SELECT 与 CREATE,解析就会立即被定向到 2 个不同的状态。但是在 LL 解析器中,选择了派生,但是在匹配输入的头部(创建或选择)之后,堆栈上剩余的东西都是“表”(加上一些东西),解析器无法区分。换句话说,LR 解析器内置了布尔值,而 LL 必须用变量指定
      • JavaCC 不是 LL(1) 或真正的 LL 任何东西。它生成递归下降解析器。默认情况下,它根据输入的下一个标记来解析选择,因此它与默认情况下的 LL(1) 非常相似,但前瞻规范的灵活性使得使用非 LL(1) 的语法变得容易。跨度>
      • 是的,我的意思是这是所有 LL(k) 解析器的限制。实际上,您的改进版本实际上有 2 个完全不相关的“表格”非终结符。 LL 切断已经匹配的终端和非终端,LR 将它们保留在堆栈中(或通过状态编码),因此有更多信息用于解析决策
      • 是的。这就是为什么每种 LR(k) 语言都是 LL(k),但反之则不然。但是,您不能从这个理论结果推断出 Bison 比 JavaCC 更具表现力,至少有三个原因。 First Bison 基于 LALR,而不是 LR(1),并且 LALR 不是 LL(1) 的超集。其次,Bison 接受不是 LALR 的语法——尽管带有警告。第三,JavaCC 只是松散地基于 LL(1),可以与许多不是 LL(1) 的语法一起使用。
      【解决方案4】:

      使用 JSqlParser V0.9.x (https://github.com/JSQLParser/JSqlParser)

      可以很好地解析您的示例
      CCJSqlParserUtil.parse("SELECT mycolumn");
      CCJSqlParserUtil.parse("SELECT mytable.mycolumn");
      CCJSqlParserUtil.parse("SELECT mydatabase.mytable.mycolumn");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多