ANTLRの文字列補間の解析
-
13-09-2019 - |
質問
私は、内部目的のために、単純な文字列操作のDSLに取り組んでいる、と私はそれがRubyで使用されているなどの文字列補間をサポートするために言語をしたいと思います。
例
name = "Bob"
msg = "Hello ${name}!"
print(msg) # prints "Hello Bob!"
私はANTLRv3の私のパーサを実装しようとするんだけど、私はANTLRを使用してかなりの経験の浅いですので、私はこの機能を実装する方法がわかりませんよ。これまでのところ、私はレクサーに私の文字列リテラルを指定したが、この場合、私は明らかに、パーサーの補間コンテンツを処理する必要があります。
私の現在の文字列リテラルの文法は次のようになります:
STRINGLITERAL : '"' ( StringEscapeSeq | ~( '\\' | '"' | '\r' | '\n' ) )* '"' ;
fragment StringEscapeSeq : '\\' ( 't' | 'n' | 'r' | '"' | '\\' | '$' | ('0'..'9')) ;
のパーサーに文字列リテラルの取り扱いを移動すると、他のすべてはそれが必要として動作を停止させるように思われます。ぞんざいなウェブ検索はすべての情報を得られませんでした。この上で開始する方法についての任意の提案ですか?
解決
私は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
はのパーサー・ルールがあるので、これはの代わりにのレクサー・ルールのの。ソースファイルをトークン化するときに空白がリテラル文字列の一部である、または無視することができ、ソースファイル内だけのスペースであればそのため、レクサーは分からないことができます。
私は少しの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
;
そして、それすべてをテストするためのmainメソッドを持つクラスます:
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!
繰り返しますが、私は専門家でないが、この(少なくとも)は、あなたがそれを解決するには、のA の方法を提供します。
HTHます。
所属していません StackOverflow