Как мне передать этот ввод?

В настоящее время у меня есть рабочий, простой язык, реализованный на Java с использованием ANTLR. Я хочу встроить его в обычный текст, аналогично 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('%>'); %>, согласно ответу Майкла Мрозека, возможно, но вне такой ситуации <% всегда будет указывать на начало блока кода.


Пример реализации

Я разработал решение, основанное на идеях, приведенных в ответе Майкла Мрозека, имитируя условия запуска Flex с использованием стробированных семантических предикатов ANTLR:

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)
    ;

person etheros    schedule 09.05.2010    source источник
comment
Значит, вы хотите создать только грамматику, которая токенизирует часть между <% и %>, игнорируя все остальное?   -  person Bart Kiers    schedule 09.05.2010
comment
Я не хочу полностью игнорировать это; Я хотел бы повторить это дословно.   -  person etheros    schedule 09.05.2010
comment
Существуют ли какие-либо ограничения на то, что может происходить за пределами <% и %>, или это может принимать любую форму? Могут ли комментарии появляться вне их (потенциально они могут содержать <% или %>)?   -  person Bart Kiers    schedule 10.05.2010
comment
Нет никаких ограничений на то, что может находиться за пределами блока <%. Я предположил, что что-то вроде <% print('%>'); %>, согласно ответу Майкла Мрозека, было бы возможно, но вне такой ситуации <% всегда будет указывать на начало блока кода.   -  person etheros    schedule 10.05.2010


Ответы (2)


Фактическая концепция выглядит хорошо, хотя маловероятно, что у вас будет токен PRINT; лексер, вероятно, выдаст что-то вроде IDENTIFIER, а синтаксический анализатор будет отвечать за определение того, что это вызов функции (например, путем поиска IDENTIFIER OPAREN ... CPAREN) и выполнения соответствующих действий.

Что касается того, как это сделать, я ничего не знаю об ANTLR, но, вероятно, в нем есть что-то вроде flex начальные условия. Если это так, вы можете заставить INITIAL начальное условие ничего не делать, кроме как искать <%, который переключится в состояние CODE, в котором определены все фактические токены; тогда "%>" переключится обратно. Во флексе это будет:

%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('%>'); %>, но, скорее всего, вы это сделаете

person Michael Mrozek    schedule 09.05.2010

но вне такой ситуации ‹% всегда будет указывать на начало блока кода.

В этом случае сначала просканируйте файл на наличие встроенного кода, а когда он у вас есть, проанализируйте встроенный код с помощью специального синтаксического анализатора (без шума перед тегами <% и после тегов %>).

ANTLR может позволить лексеру анализировать только (небольшую) часть входного файла и игнорировать остальную часть. Обратите внимание, что в этом случае вы не можете создать «комбинированную грамматику» (парсер и лексер в одном). Вот как можно создать такой «частичный лексер»:

// 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! %>
person Bart Kiers    schedule 13.05.2010