문제

내부 목적으로 간단한 문자열 조작 DSL을 작업하고 있으며 루비에서 사용되는 문자열 보간을 지원하는 언어를 원합니다.

예를 들어:

name = "Bob"
msg = "Hello ${name}!"
print(msg)   # prints "Hello Bob!"

ANTLRV3에서 구문 분석기를 구현하려고하지만 AntlR을 사용하는 것이 경험이 없으므로이 기능을 구현하는 방법은 확실하지 않습니다. 지금까지 Lexer에서 문자 리터럴을 지정했지만이 경우 구문 분석기의 보간 내용을 처리해야합니다.

내 현재 문자열 문자 문법은 다음과 같습니다.

STRINGLITERAL : '"' ( StringEscapeSeq | ~( '\\' | '"' | '\r' | '\n' ) )* '"' ;
fragment StringEscapeSeq : '\\' ( 't' | 'n' | 'r' | '"' | '\\' | '$' | ('0'..'9')) ;

문자열 리터럴 취급을 파서로 옮기면 다른 모든 것이 작동하지 않는 것으로 보입니다. Cursory Web Search는 정보를 얻지 못했습니다. 이것에 대한 시작 방법에 대한 제안이 있습니까?

도움이 되었습니까?

해결책

나는 Antlr 전문가가 아니지만 여기에 가능한 문법이 있습니다.

grammar Str;

parse
    :    ((Space)* statement (Space)* ';')+ (Space)* EOF
    ;

statement
    :    print | assignment
    ;

print
    :    'print' '(' (Identifier | stringLiteral) ')' 
    ;

assignment
    :    Identifier (Space)* '=' (Space)* stringLiteral
    ;

stringLiteral
    :    '"' (Identifier | EscapeSequence | NormalChar | Space | Interpolation)* '"'
    ;

Interpolation
    :    '${' Identifier '}'
    ;

Identifier
    :    ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
    ;

EscapeSequence
    :    '\\' SpecialChar
    ;

SpecialChar
    :     '"' | '\\' | '$'
    ;

Space
    :    (' ' | '\t' | '\r' | '\n')
    ;

NormalChar
    :    ~SpecialChar
    ;

아시다시피, 몇 가지가 있습니다 (Space)*-예제 문법 내부의 es. 이거 때문입니다 stringLiteral a 파서-룰 대신 a 렉서-룰. 따라서 소스 파일을 토큰 화 할 때 Lexer는 공백이 문자열 리터럴의 일부인지 또는 소스 파일 내부의 공간인지 알 수 없습니다.

나는 작은 Java 클래스로 예제를 테스트했으며 모두 예상대로 작동했습니다.

/* the same grammar, but now with a bit of Java code in it */
grammar Str;

@parser::header {
    package antlrdemo;
    import java.util.HashMap;
}

@lexer::header {
    package antlrdemo;
}

@parser::members {
    HashMap<String, String> vars = new HashMap<String, String>();
}

parse
    :    ((Space)* statement (Space)* ';')+ (Space)* EOF
    ;

statement
    :    print | assignment
    ;

print
    :    'print' '(' 
         (    id=Identifier    {System.out.println("> "+vars.get($id.text));} 
         |    st=stringLiteral {System.out.println("> "+$st.value);}
         ) 
         ')' 
    ;

assignment
    :    id=Identifier (Space)* '=' (Space)* st=stringLiteral {vars.put($id.text, $st.value);}
    ;

stringLiteral returns [String value]
    :    '"'
        {StringBuilder b = new StringBuilder();} 
        (    id=Identifier           {b.append($id.text);}
        |    es=EscapeSequence       {b.append($es.text);}
        |    ch=(NormalChar | Space) {b.append($ch.text);}
        |    in=Interpolation        {b.append(vars.get($in.text.substring(2, $in.text.length()-1)));}
        )* 
        '"'
        {$value = b.toString();}
    ;

Interpolation
    :    '${' i=Identifier '}'
    ;

Identifier
    :    ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
    ;

EscapeSequence
    :    '\\' SpecialChar
    ;

SpecialChar
    :     '"' | '\\' | '$'
    ;

Space
    :    (' ' | '\t' | '\r' | '\n')
    ;

NormalChar
    :    ~SpecialChar
    ;

그리고 모든 것을 테스트하는 주요 방법이있는 클래스 :

package antlrdemo;

import org.antlr.runtime.*;

public class ANTLRDemo {
    public static void main(String[] args) throws RecognitionException {
        String source = "name = \"Bob\";        \n"+
                "msg = \"Hello ${name}\";       \n"+
                "print(msg);                    \n"+
                "print(\"Bye \\${for} now!\");    ";
        ANTLRStringStream in = new ANTLRStringStream(source);
        StrLexer lexer = new StrLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        StrParser parser = new StrParser(tokens);
        parser.parse();
    }
}

다음 출력을 생성합니다.

> Hello Bob
> Bye \${for} now!

다시 말하지만, 나는 전문가가 아니지만, 이것은 (적어도) 당신에게 그것을 해결하는 방법.

HTH.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top