2014-06-12 18 views
5

Java'da bir kod çevirmeni Antlr4 yardımıyla yazmaya çalışıyorum ve dilbilgisi bölümü ile bugüne kadar büyük başarılar kazandım. Ancak şimdi kafamı, girdim ayrıştırıldıktan sonra üzerinde çalışmam gereken ayrıştırıcı ağacı veri yapısının etrafına aklımı saran bir duvara çarpıyorum.Antlr4'teki içerik veri yapısını anlama

Ayrıştırma ağacım üzerinden gitmek için ziyaretçi şablonunu kullanmaya çalışıyorum. Kargaşamın noktalarını gösterecek bir örnek göstereceğim.

Benim dilbilgisi:

grammar pqlc; 

// Lexer 

//Schlüsselwörter 
EXISTS: 'exists'; 
REDUCE: 'reduce'; 
QUERY: 'query'; 
INT: 'int'; 
DOUBLE: 'double'; 
CONST: 'const'; 
STDVECTOR: 'std::vector'; 
STDMAP: 'std::map'; 
STDSET: 'std::set'; 
C_EXPR: 'c_expr'; 

INTEGER_LITERAL : (DIGIT)+ ; 
fragment DIGIT: '0'..'9'; 
DOUBLE_LITERAL : DIGIT '.' DIGIT+; 

LPAREN   : '('; 
RPAREN   : ')'; 
LBRACK   : '['; 
RBRACK   : ']'; 
DOT    : '.'; 
EQUAL   : '=='; 
LE    : '<='; 
GE    : '>='; 
GT    : '>'; 
LT    : '<'; 
ADD    : '+'; 
MUL    : '*'; 
AND    : '&&'; 
COLON   : ':'; 

IDENTIFIER : JavaLetter JavaLetterOrDigit*; 
fragment JavaLetter : [a-zA-Z$_]; // these are the "java letters" below 0xFF 
fragment JavaLetterOrDigit : [a-zA-Z0-9$_]; // these are the "java letters or digits" below 0xFF 
WS 
    : [ \t\r\n\u000C]+ -> skip 
    ; 
COMMENT 
    : '/*' .*? '*/' -> skip 
    ; 

LINE_COMMENT 
    : '//' ~[\r\n]* -> skip 
    ; 


// Parser 

//start_rule: query; 

query : 
     quant_expr 
     | qexpr+ 
     | IDENTIFIER // order IDENTIFIER and qexpr+? 
     | numeral 
     | c_expr //TODO 

     ; 

c_type : INT | DOUBLE | CONST; 
bin_op: AND | ADD | MUL | EQUAL | LT | GT | LE| GE; 


qexpr: 
     LPAREN query RPAREN bin_op_query? 
     // query bin_op query 
     | IDENTIFIER bin_op_query? // copied from query to resolve left recursion problem 
     | numeral bin_op_query? //^
     | quant_expr bin_op_query? //^
      |c_expr bin_op_query? 
      // query.find(query) 
     | IDENTIFIER find_query? // copied from query to resolve left recursion problem 
     | numeral find_query? //^
     | quant_expr find_query? 
      |c_expr find_query? 
      // query[query] 
      | IDENTIFIER array_query? // copied from query to resolve left recursion problem 
     | numeral array_query? //^
     | quant_expr array_query? 
      |c_expr array_query? 

    // | qexpr bin_op_query // bad, resolved by quexpr+ in query 
    ; 

bin_op_query: bin_op query bin_op_query?; // resolve left recursion of query bin_op query 

find_query: '.''find' LPAREN query RPAREN; 
array_query: LBRACK query RBRACK; 

quant_expr: 
    quant id ':' query 
      | QUERY LPAREN match RPAREN ':' query 
      | REDUCE LPAREN IDENTIFIER RPAREN id ':' query 
    ; 

match: 
     STDVECTOR LBRACK id RBRACK EQUAL cm 
    | STDMAP '.''find' LPAREN cm RPAREN EQUAL cm 
    | STDSET '.''find' LPAREN cm RPAREN 
    ; 

cm: 
    IDENTIFIER 
    | numeral 
    | c_expr //TODO 
    ; 

quant : 
      EXISTS; 

id : 
    c_type IDENTIFIER 
    | IDENTIFIER // Nach Seite 2 aber nicht der Übersicht. Laut übersicht id -> aber dann wäre Regel 1 ohne + 
    ; 

numeral : 
      INTEGER_LITERAL 
     | DOUBLE_LITERAL 
     ; 
c_expr: 
      C_EXPR 
     ; 
Şimdi

en aşağıdaki dizeyi ayrıştırmak edelim: tree

benim ziyaretçi visitQexpr(@NotNull pqlcParser.QexprContext ctx) olduğunu varsayalım:

double x: x >= c_expr 

Görme bu ağacı alırsınız Qexpr (x bin_op_query) dalına ulaştığında rutin.

Soruma göre, soldaki çocukların ("x" ağacında) bir terminal düğümü veya daha özel olarak "IDENTIFIER" olduğunu nasıl söyleyebilirim? Kurallar olmadığından Terminal düğümleri için ziyaret kuralları yoktur. ctx.getChild(0)'un RuleIndex'i yoktur. Terminalde olup olmadığımı kontrol etmek için kullanabilirim, fakat IDENTIFIER ya da başka tür terminal belirteçlerinde olduğumu söyleyemem. Farkı bir şekilde söyleyebilmem lazım.

Daha fazla sorularım vardı, ancak açıklamalarımı yazmam için gereken süreyi unuttum: < Şimdiden teşekkürler.

id : 
    c_type labelA = IDENTIFIER 
    | labelB = IDENTIFIER 
    ; 

Ayrıca farklı ziyaretleri yaratmak için bu yapabilirdi:

id : 
    c_type IDENTIFIER #idType1 //choose more appropriate names! 
    | IDENTIFIER   #idType2 
    ; 

Bu yaratacak

+0

Genel çözüm, ayrıştırma ağacını ziyaret etmek yerine [AST (soyut sözdizim ağacı)] (https://en.wikipedia.org/wiki/Abstract_syntax_tree) adresini ziyaret etmektir ... Ancak bu özellik [kaldırıldı beri antlr4] (http://stackoverflow.com/questions/15823333/how-can-i-build-an-ast-using-antlr4).Belki de x sembolü olan bir simge olup olmadığını öğrenmek için [sembol tablosu] (https://en.wikipedia.org/wiki/Symbol_table) kralı kullanabilirsiniz :) İyi şanslar! – NiziL

+0

Çocuğun payLoad() öğesini alırsam, belirteç kimliği (diğer öğeler arasında) bulunur. Böylece bilgi veri yapısında saklanır, kolayca erişebilmek için uygun bir yol olmalıdır. ctx, ctx.IDENTIFIER() gibi farklı terminal düğümlerim için işlevleri var ancak tam olarak ne yaptığını anlatamam. Görünüşe göre ctx.IDENTIFIER() dönüş değeri, düğüm düğümleri ve diğer düğümlerin metni yoksa boştur. Yine de, birden çok çocuk varsa/hangisinin uçbirim düğümü olduğunu söylemez. Tüm veri yapısını böylesine kafa karıştırıcı buluyorum: \ – user2323596

+1

Bazı Bağlamda bir alt ağ/terminal düğümünün ziyaret edilip edilmediğini öğrenmek istediğimde, genellikle bu subrule/terminalin boş olup olmadığını (ziyaret edilmiyor) veya boş olmadığını kontrol ediyorum. (ziyaret ediliyor). Ama aynı zamanda bu oldukça hacky'i de, istediğini elde etmek için güzel ve temiz bir yoldan daha iyi buluyorum ... – schauk11erd

cevap

3

Onların çevredeki bağlamda mevcut olmadığını kontrol/jeton etiket eklemek ve bunlara erişebilirsiniz iki alternatif için farklı ziyaretçiler ve varsayalım (yani doğrulamamış) id için ziyaretçi çağrılmayacak.

olsa şu yaklaşım tercih:

id : 
     typeDef 
    | otherId 
    ; 
typeDef: c_type IDENTIFIER; 
otherId : IDENTIFIER ; 

Bu daha ağır daktilo sistemdir. Ama özellikle düğümleri ziyaret edebilirsiniz. Kullandığım bazı temel kurallar:

  1. Yalnızca tüm alternatifler ayrıştırıcı kuralları olduğunda |'u kullanın.
  2. Her Jetonu "daha anlamlı" olarak vermek için bir ayrıştırıcı kuralına (otherId gibi) sarın.
  3. Simgelerin gerçekten önemli olmadığı (örneğin ;) ve bu nedenle ayrıştırma ağacında gerekli olmadığında, parser kurallarını ve jetonlarını karıştırmak tamamdır.
İlgili konular