SORU
9 Aralık 2010, PERŞEMBE


ANTLR kullanarak 3.3?

ANTLR ve C ile başlamak için çalışıyorum ama olağanüstü zor belgeleri/öğreticiler eksikliği nedeniyle buluyorum. Eski sürümleri için gönülsüz birkaç öğreticiler buldum, ama bu yana API için bazı büyük değişiklikler olmuştur görünüyor.

Kimse bana bir dilbilgisi oluşturmak ve kısa bir programı kullanmak için basit bir örnek verebilir misiniz?

Yaptım sonunda başardı benim dilbilgisi dosyası derleme içine bir lexer ve ayrıştırıcı, ve ben o derlenmiş ve çalışan Visual Studio (sonra olması için derlemeye ANTLR kaynak çünkü C# ikililer gibi güncel değil! kaynak söylemeye gerek bile yok-bazı düzeltmeler) olmadan derleme değildir, ama yine de ayrıştırıcı/yapmak lexer sınıfları ne hiçbir fikrim yok. Sözde bazı giriş belirli bir AST üretebilir. ve sonra bir şey ile fantezi yapmak gerekir.

CEVAP
9 Aralık 2010, PERŞEMBE


Hadi aşağıdaki simgeleri oluşan basit ifadeler ayrıştırmak için istediğini söyle

  • - çıkarma (tekli);
  • ayrıca;
  • * çarpma;
  • / bölümü;
  • (...) gruplandırma (alt) ifadeler;
  • tamsayı ve ondalık sayılar.

ANTLR bir gramer şöyle:

grammar Expression;

options {
  language=CSharp2;
}

parse
  :  exp EOF 
  ;

exp
  :  addExp
  ;

addExp
  :  mulExp ((' ' | '-') mulExp)*
  ;

mulExp
  :  unaryExp (('*' | '/') unaryExp)*
  ;

unaryExp
  :  '-' atom 
  |  atom
  ;

atom
  :  Number
  |  '(' exp ')' 
  ;

Number
  :  ('0'..'9')  ('.' ('0'..'9') )?
  ;

Şimdi oluşturmak için uygun bir AST, ekleyin output=AST; options { ... } bölüm ve karışık kaset "ağaç operatörleri" gramer tanımlama hangi belirteçleri olmalıdır kökü bir ağaç. Bunu yapmak için iki yol vardır:

  1. senin jeton sonra ^ ! ekleyin. ^ belirteç bir kök neden olur ve ! ast belirteci, dışlar;
  2. kullanarak "kurallar": ... -> ^(Root Child Child ...) yeniden yazarsın.

Örneğin 24**: kural

foo
  :  TokenA TokenB TokenC TokenD
  ;

ve hadi TokenB kök TokenA TokenC kendi çocukları olmak istediğini söyle, ve ağaçtan TokenD hariç tutmak istiyorum. Seçenek 1 kullanarak bunu yapmak için:

foo
  :  TokenA TokenB^ TokenC TokenD!
  ;

ve burada 2 seçeneği kullanarak bunu yapmak için nasıl:

foo
  :  TokenA TokenB TokenC TokenD -> ^(TokenB TokenA TokenC)
  ;

Yani, burada ağaç operatörleri ile dilbilgisi:

grammar Expression;

options {
  language=CSharp2;
  output=AST;
}

tokens {
  ROOT;
  UNARY_MIN;
}

@parser::namespace { Demo.Antlr }
@lexer::namespace { Demo.Antlr }

parse
  :  exp EOF -> ^(ROOT exp)
  ;

exp
  :  addExp
  ;

addExp
  :  mulExp ((' ' | '-')^ mulExp)*
  ;

mulExp
  :  unaryExp (('*' | '/')^ unaryExp)*
  ;

unaryExp
  :  '-' atom -> ^(UNARY_MIN atom)
  |  atom
  ;

atom
  :  Number
  |  '(' exp ')' -> exp
  ;

Number
  :  ('0'..'9')  ('.' ('0'..'9') )?
  ;

Space 
  :  (' ' | '\t' | '\r' | '\n'){Skip();}
  ;

Ben de Space kaynak dosyada herhangi bir beyaz boşluk görmezden bir kural eklendi ve lexer ve ayrıştırıcı için bazı ekstra belirteçleri ve isim eklendi. Sırası önemli (options { ... } önce sonra tokens { ... } ve son olarak @... {}-ad alanı bildirimi) olduğunu unutmayın.

İşte bu kadar.

Şimdi dilbilgisi dosyası: lexer ve ayrıştırıcı oluşturmak

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

ve projenizde .cs dosyaları bir arada C# runtime DLL's ile koyun.

Aşağıdaki sınıfını kullanarak bunu test edebilirsiniz:

using System;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using Antlr.StringTemplate;

namespace Demo.Antlr
{
  class MainClass
  {
    public static void Preorder(ITree Tree, int Depth) 
    {
      if(Tree == null)
      {
        return;
      }

      for (int i = 0; i < Depth; i  )
      {
        Console.Write("  ");
      }

      Console.WriteLine(Tree);

      Preorder(Tree.GetChild(0), Depth   1);
      Preorder(Tree.GetChild(1), Depth   1);
    }

    public static void Main (string[] args)
    {
      ANTLRStringStream Input = new ANTLRStringStream("(12.5   56 / -7) * 0.5"); 
      ExpressionLexer Lexer = new ExpressionLexer(Input);
      CommonTokenStream Tokens = new CommonTokenStream(Lexer);
      ExpressionParser Parser = new ExpressionParser(Tokens);
      ExpressionParser.parse_return ParseReturn = Parser.parse();
      CommonTree Tree = (CommonTree)ParseReturn.Tree;
      Preorder(Tree, 0);
    }
  }
}

aşağıdaki çıktıyı üretir:

ROOT
  *
     
      12.5
      /
        56
        UNARY_MIN
          7
    0.5

aşağıdaki AST karşılık gelir:

alt text

(diyagram* *43) kullanılarak oluşturulan

ANTLR 3.3 sadece serbest bırakıldı ve Bulunan hedef olduğunu unutmayın "beta". Bu ANTLR 3.2 benim örnek olarak kullandım.

Oldukça basit bir dil durumunda (örnek yukarıda), aynı zamanda bir AST oluşturmadan anında sonuç değerlendirebilirsiniz. Düz C gömerek yapabilirsin# dilbilgisi kod içinde dosya ve ayrıştırıcı kuralları izin belirli bir değeri döndürür.

İşte size bir örnek:

grammar Expression;

options {
  language=CSharp2;
}

@parser::namespace { Demo.Antlr }
@lexer::namespace { Demo.Antlr }

parse returns [double value]
  :  exp EOF {$value = $exp.value;}
  ;

exp returns [double value]
  :  addExp {$value = $addExp.value;}
  ;

addExp returns [double value]
  :  a=mulExp       {$value = $a.value;}
     ( ' ' b=mulExp {$value  = $b.value;}
     | '-' b=mulExp {$value -= $b.value;}
     )*
  ;

mulExp returns [double value]
  :  a=unaryExp       {$value = $a.value;}
     ( '*' b=unaryExp {$value *= $b.value;}
     | '/' b=unaryExp {$value /= $b.value;}
     )*
  ;

unaryExp returns [double value]
  :  '-' atom {$value = -1.0 * $atom.value;}
  |  atom     {$value = $atom.value;}
  ;

atom returns [double value]
  :  Number      {$value = Double.Parse($Number.Text, CultureInfo.InvariantCulture);}
  |  '(' exp ')' {$value = $exp.value;}
  ;

Number
  :  ('0'..'9')  ('.' ('0'..'9') )?
  ;

Space 
  :  (' ' | '\t' | '\r' | '\n'){Skip();}
  ;

sınıf ile test edilebilir:

using System;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using Antlr.StringTemplate;

namespace Demo.Antlr
{
  class MainClass
  {
    public static void Main (string[] args)
    {
      string expression = "(12.5   56 / -7) * 0.5";
      ANTLRStringStream Input = new ANTLRStringStream(expression);  
      ExpressionLexer Lexer = new ExpressionLexer(Input);
      CommonTokenStream Tokens = new CommonTokenStream(Lexer);
      ExpressionParser Parser = new ExpressionParser(Tokens);
      Console.WriteLine(expression   " = "   Parser.parse());
    }
  }
}

ve aşağıdaki çıktıyı üretir:

(12.5   56 / -7) * 0.5 = 2.25

EDİT

Yorum, Ralph yazdı:

İpucu için bu kullanarak Visual Studio: sen-ebilmek koymak gibi bir şey java -cp "$(ProjectDir)antlr-3.2.jar" org.antlr.Tool "$(ProjectDir)Expression.g" pre-build olayları, sonra sadece değiştirmek dilbilgisi ve çalıştırmak, proje hakkında endişelenmenize gerek kalmadan yeniden lexer/parser.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Kyler Briskey

    Kyler Briske

    20 ŞUBAT 2011
  • New Scientist

    New Scientis

    27 Kasım 2006
  • Paul Schroder

    Paul Schrode

    30 Kasım 2007