Zor yol
Özyinelemeli bir iniş ayrıştırıcısı istiyorsunuz .
Öncelik elde etmek için, örneğin örnek dizenizi kullanarak özyinelemeli düşünmeniz gerekir.
1+11*5
bunu elle yapmak için 1
, okumanız , sonra artıyı görmeniz ve 11
... ile başlayan yepyeni bir özyinelemeli ayrıştırma "oturumu" başlatmanız ve 11 * 5
bunu kendi faktörüne ayırıp bir ayrıştırma ağacı oluşturduğunuzdan emin olmanız gerekir 1 + (11 * 5)
.
Tüm bunlar, özellikle C'nin ek güçsüzlüğüyle, açıklamaya çalışmak çok acı verici. Bakın, 11'i ayrıştırdıktan sonra, eğer * gerçekten bir + ise, bir terim oluşturma girişiminden vazgeçmeniz ve bunun yerine ayrıştırmanız gerekir. 11
kendisi bir faktör olarak. Kafam zaten patlıyor. Yinelemeli düzgün strateji ile mümkündür, ancak daha iyi bir yol var ...
Kolay (doğru) yol
Bison gibi bir GPL aracı kullanıyorsanız, muhtemelen bison tarafından üretilen C kodu GPL tarafından kapsanmadığından lisans sorunları hakkında endişelenmenize gerek yoktur (IANAL ancak GPL araçlarının GPL'yi oluşturulan kod / ikili dosyalar; örneğin Apple, GCC ile Aperture gibi bir kod derler ve söz konusu kodu GPL'ye gerek kalmadan satarlar).
Bison'u (veya eşdeğer bir şey, ANTLR vb.) İndirin .
Genellikle bison çalıştırıp bu dört işlevli hesap makinesini gösteren istediğiniz C kodunu alabileceğiniz bazı örnek kodlar vardır:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Oluşturulan koda bakın ve bunun göründüğü kadar kolay olmadığını görün. Ayrıca, Bison gibi bir araç kullanmanın avantajları şunlardır: 1) bir şeyler öğrenirsiniz (özellikle Dragon kitabını okursanız ve gramerler hakkında bilgi alırsanız), 2) NIH'nin tekerleği yeniden icat etmeye çalışmasından kaçınırsınız . Gerçek bir ayrıştırıcı-oluşturucu aracıyla, aslında daha sonra ölçeklendirme konusunda bir ümidiniz olur ve diğer insanlara ayrıştırıcıların ayrıştırma araçlarının alanı olduğunu bildiğinizi gösterir.
Güncelleme:
Buradaki insanlar çok sağlam tavsiyeler verdiler. Ayrıştırma araçlarını atlamaya veya sadece Shunting Yard algoritmasını veya elle yuvarlanmış özyinelemeli düzgün ayrıştırıcıyı kullanmaya karşı tek uyarım, küçük oyuncak dillerin 1 bir gün işlevler (sin, cos, log) ve değişkenler, koşullar ve koşullar ile büyük gerçek dillere dönüşebileceğidir. döngüler.
Flex / Bison, küçük, basit bir yorumlayıcı için çok iyi olabilir, ancak bir defaya mahsus ayrıştırıcı + değerlendirici, değişikliklerin yapılması veya özelliklerin eklenmesi gerektiğinde hattın aşağısında sorunlara neden olabilir. Durumunuz değişecek ve muhakemenizi kullanmanız gerekecek; sadece günahlarınız için başkalarını cezalandırmayın [2] ve gereğinden az bir alet geliştirin.
Ayrıştırma için en sevdiğim araç
İş için dünyadaki en iyi araç, Haskell programlama dili ile birlikte gelen Parsec kitaplığıdır (özyinelemeli düzgün ayrıştırıcılar için). BNF'ye çok benziyor veya ayrıştırma için özel bir araç veya alana özgü bir dil gibi görünüyor (örnek kod [3]), ancak aslında Haskell'de normal bir kitaplıktır, yani diğerleriyle aynı derleme adımında derlenir. Haskell kodunuzu değiştirebilir ve rastgele Haskell kodu yazabilir ve onu ayrıştırıcınızda çağırabilir ve diğer kitaplıkları aynı kodda karıştırabilir ve eşleştirebilirsiniz . (Bunun gibi bir ayrıştırma dilini Haskell dışında bir dilde gömmek, bu arada bir sürü sözdizimsel bozukluğa neden olur. Bunu C # ile yaptım ve oldukça iyi çalışıyor ama o kadar güzel ve kısa değil.)
Notlar:
1 Richard Stallman, Neden Tcl kullanmamalısınız?
Emacs'ın temel dersi, bir uzantı dilinin yalnızca bir "uzantı dili" olmaması gerektiğidir. Önemli programları yazmak ve sürdürmek için tasarlanmış gerçek bir programlama dili olmalıdır. Çünkü insanlar bunu yapmak isteyecek!
[2] Evet, bu "dili" kullanmaktan sonsuza kadar yaralandım.
Ayrıca, bu girişi gönderdiğimde önizlemenin doğru olduğunu, ancak SO'ların ilk paragraftaki yakın bağlantı etiketimi yediğini ve ayrıştırıcıların önemsiz bir şey olmadığını kanıtladığını unutmayın, çünkü normal ifadeler kullanırsanız ve tek seferlik saldırılar yaparsanız muhtemelen ince ve küçük bir şeyi yanlış anlayacaktır .
[3] Parsec kullanan bir Haskell ayrıştırıcısının parçacığı: üsler, parantezler, çarpma için boşluklar ve sabitler (pi ve e gibi) ile genişletilmiş dört işlevli bir hesap makinesi.
aexpr = expr `chainl1` toOp
expr = optChainl1 term addop (toScalar 0)
term = factor `chainl1` mulop
factor = sexpr `chainr1` powop
sexpr = parens aexpr
<|> scalar
<|> ident
powop = sym "^" >>= return . (B Pow)
<|> sym "^-" >>= return . (\x y -> B Pow x (B Sub (toScalar 0) y))
toOp = sym "->" >>= return . (B To)
mulop = sym "*" >>= return . (B Mul)
<|> sym "/" >>= return . (B Div)
<|> sym "%" >>= return . (B Mod)
<|> return . (B Mul)
addop = sym "+" >>= return . (B Add)
<|> sym "-" >>= return . (B Sub)
scalar = number >>= return . toScalar
ident = literal >>= return . Lit
parens p = do
lparen
result <- p
rparen
return result