代码之家  ›  专栏  ›  技术社区  ›  etheros

我该如何处理这些输入?

  •  3
  • etheros  · 技术社区  · 15 年前

    我目前使用ANTLR在Java中实现了一种工作的、简单的语言。我想做的是将它嵌入到纯文本中,以类似于PHP的方式。

    Lorem ipsum dolor sit amet
    <% print('consectetur adipiscing elit'); %>
    Phasellus volutpat dignissim sapien.
    

    我预计生成的令牌流将类似于:

    CDATA OPEN PRINT OPAREN APOS STRING APOS CPAREN SEMI CLOSE CDATA
    

    不受限制的可能是 <% 阻止。我猜是这样的 <% print('%>'); %> <% 总是表示代码块的开始。


    示例实现

    lexer grammar Lexer;
    
    @members {
        boolean codeMode = false;
    }
    
    OPEN    : {!codeMode}?=> '<%' { codeMode = true; } ;
    CLOSE   : {codeMode}?=> '%>' { codeMode = false;} ;
    LPAREN  : {codeMode}?=> '(';
    //etc.
    
    CHAR    : {!codeMode}?=> ~('<%');
    
    
    parser grammar Parser;
    
    options {
        tokenVocab = Lexer;
        output = AST;
    }
    
    tokens {
        VERBATIM;
    }
    
    program :
        (code | verbatim)+
        ;   
    
    code :
        OPEN statement+ CLOSE -> statement+
        ;
    
    verbatim :
        CHAR -> ^(VERBATIM CHAR)
        ;
    
    2 回复  |  直到 15 年前
        1
  •  1
  •   rici    8 年前

    实际的概念看起来不错,虽然你不太可能有一个打印令牌;lexer可能会发出类似IDENTIFIER的消息,解析器负责找出它是一个函数调用(例如,通过查找 IDENTIFIER OPAREN ... CPAREN

    至于如何做到这一点,我不知道任何关于ANTLR,但它可能有类似flex的东西 start conditions INITIAL 开始条件除了寻找 <% ,将切换到 CODE 说明所有实际令牌的定义位置;然后“%>”会切换回来。在flex中是:

    %s CODE
    
    %%
    
    <INITIAL>{
        "<%"      {BEGIN(CODE);}
        .         {}
    }
    
     /* All these are implicitly in CODE because it was declared %s,
        but you could wrap it in <CODE>{} too
      */
    "%>"          {BEGIN(INITIAL);}
    "("           {return OPAREN;}
    "'"           {return APOS;}
    ...
    

    %> 在不是结束标记的上下文中,比如在字符串中;如果你想允许 <% print('%>'); %> ,但很可能是这样

        2
  •  2
  •   Bart Kiers    15 年前

    但在这种情况之外,<%总是表示代码块的开始。

    在这种情况下,首先扫描文件中的嵌入代码,一旦有了这些代码,就用一个专用的解析器解析嵌入的代码(在测试之前没有噪音) <% 之后呢 %> 标签)。

    ANTLR可以选择让lexer只解析输入文件的一小部分,而忽略其余部分。注意,在这种情况下不能创建“组合语法”(解析器和词法器合一)。下面是如何创建这样一个“部分lexer”:

    // file EmbeddedCodeLexer.g
    lexer grammar EmbeddedCodeLexer;
    
    options{filter=true;} // <- enables the partial lexing!
    
    EmbeddedCode
      :  '<%'                            // match an open tag
         (  String                       // ( match a string literal
         |  ~('%' | '\'')                //   OR match any char except `%` and `'`
         |  {input.LT(2) != '>'}?=> '%'  //   OR only match a `%` if `>` is not ahead of it
         )*                              // ) <- zero or more times
         '%>'                            // match a close tag
      ;
    
    fragment
    String
      :  '\'' ('\\' . | ~('\'' | '\\'))* '\''
      ;
    

    java -cp antlr-3.2.jar org.antlr.Tool EmbeddedCodeLexer.g 
    

    做一个小测试:

    import org.antlr.runtime.*;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            String source = "Lorem ipsum dolor sit amet       \n"+
                    "<%                                       \n"+
                    "a = 2 > 1 && 10 % 3;                     \n"+
                    "print('consectetur %> adipiscing elit'); \n"+
                    "%>                                       \n"+
                    "Phasellus volutpat dignissim sapien.     \n"+
                    "foo <% more code! %> bar                 \n";
            ANTLRStringStream in = new ANTLRStringStream(source);
            EmbeddedCodeLexer lexer = new EmbeddedCodeLexer(in);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            for(Object o : tokens.getTokens()) {
                System.out.println("=======================================\n"+
                        "EmbeddedCode = "+((Token)o).getText());
            }
        }
    }
    

    全部编译:

    javac -cp antlr-3.2.jar *.java
    

    最后运行主类:

    // *nix/MacOS
    java -cp .:antlr-3.2.jar Main
    
    // Windows
    java -cp .;antlr-3.2.jar Main 
    

    它将产生以下输出:

    =======================================
    EmbeddedCode = <%                                       
    a = 2 > 1 && 10 % 3;                     
    print('consectetur %> adipiscing elit'); 
    %>
    =======================================
    EmbeddedCode = <% more code! %>