Semantik: attribute grammar ve weakest precondition
Static semantics için attribute grammar (synthesized, inherited, intrinsic), dinamik semantiğin üç yöntemi ve axiomatic semantics ile weakest precondition hesabı.
Sözdizimi (syntax) bir programın biçimini tanımlar; ama “değişken kullanılmadan önce bildirilmeli” ya da “int’e real atanamaz” gibi kurallar BNF ile yazılamaz. Bu sayfa iki şeyi kapsar: bu kuralları yazmak için attribute grammar ve programın anlamını (semantics) tanımlamak için üç yöntem. Sınavda en sık çıkan iki soru tipi: attribute grammar ile tip kontrolü yapmak ve weakest precondition hesaplamak.
Altın kurallar:
- Synthesized yukarı, inherited aşağı akar.
- Önce look-up, sonra expected_type, sonra actual_type, en son predicate.
- Weakest precondition sondan başa hesaplanır. Postcondition’daki değişkenin yerine atamanın sağ tarafını yaz.
- Negatif sayıyla bölünce eşitsizlik yön değiştirir.
Static semantics
Bazı kurallar derleme zamanında kontrol edilir ama context-free grammar ile ifade edilemez ya da edilmesi çok zahmetlidir:
- Java’da float bir değer int değişkene atanamaz.
- Tüm değişkenler kullanılmadan önce bildirilmeli.
- Ada’da bir alt programın sonundaki
endadı, başındaki adla aynı olmalı.
Bunlara static semantics denir: anlamla ilgili ama çalıştırmadan, derleme zamanında kontrol edilebilen kurallar. Adı “semantics” olsa da aslında sözdizimine yakındır.
Attribute grammar
Attribute grammar, bir CFG’ye üç şey ekler:
- Attribute’lar: her sembole bağlı değerler (
actual_type,expected_type). - Semantic function’lar (attribute computation functions): her kurala bağlı, attribute’ların nasıl hesaplandığını söyleyen kurallar.
- Predicate’ler: kuralın geçerli olması için doğru olması gereken koşullar. Biri false olursa static semantics hatası var.
Üç tür attribute var:
| Tür | Akış yönü | Örnek |
|---|---|---|
| Synthesized | Çocuklardan ebeveyne, yukarı | <expr>.actual_type operandlardan hesaplanır |
| Inherited | Ebeveynden/kardeşten çocuğa, aşağı veya yana | <expr>.expected_type, kardeşi <var>’dan gelir |
| Intrinsic | Yapraklarda, dışarıdan | look-up(<var>.string) symbol table’dan |
Tip kontrolü örneği
1. <assign> -> <var> = <expr>
<expr>.expected_type <- <var>.actual_type
2. <expr> -> <var>[2] + <var>[3]
<expr>.actual_type <- if (<var>[2].actual_type = int) and
(<var>[3].actual_type = int) then int else real
predicate: <expr>.actual_type == <expr>.expected_type
3. <expr> -> <var>
<expr>.actual_type <- <var>.actual_type
predicate: <expr>.actual_type == <expr>.expected_type
4. <var> -> A | B | C
<var>.actual_type <- look-up(<var>.string)
Köşeli parantezli indisler (<var>[2]) aynı nonterminal’in farklı örneklerini ayırır. A = B + C için A real, B real, C int olsun:
adım değer
1. <var>.actual_type <- look-up(A) real (intrinsic)
2. <expr>.expected_type <- real real (inherited)
3. <var>[2] = look-up(B), <var>[3] real, int (intrinsic)
4. <expr>.actual_type real (synthesized: ikisi de int değil)
5. predicate real == real TRUE
A int, B int, C real olsaydı: expected int, actual real, predicate false. Bu gramer otomatik dönüşüm (coercion) yapmaz; A real, B int, C int bile hata verir çünkü actual int, expected real.
Bağlamdan bağımsız olmayan dil
dili CFG ile yazılamaz. Attribute grammar ile kolay:
<S> -> <A> <B> <C> predicate: <A>.size == <B>.size == <C>.size
<A> -> a | <A>[2] a <A>.size <- 1 | <A>.size <- <A>[2].size + 1
(B ve C için aynı)
CFG kısmı a+b+c+ biçimini kabul eder. aabbcc geçer; aabbc sözdizimsel olarak doğru ama predicate false.
Dinamik semantik: üç yöntem
| Yöntem | Anlam neyle tanımlanır | Kim kullanır |
|---|---|---|
| Operational | Programın (sanal) bir makinede çalışırken yaptığı durum değişiklikleri | Dil kılavuzları, öğretim |
| Denotational | Her yapıyı matematiksel bir nesneye/fonksiyona eşlemek | Teorik çalışma, kanıt, derleyici üretimi |
| Axiomatic | İfadelerden önce ve sonra doğru olan mantık önermeleri | Program doğrulama |
Operational semantics
Yüksek seviyeli bir yapının anlamını, daha basit ve iyi bilinen bir dile çevirerek verir. C’nin for döngüsü:
for (expr1; expr2; expr3) { ... }
expr1;
loop: if expr2 == 0 goto out
...
expr3;
goto loop
out: ...
Okuyan biri for’u bilmese de if ve goto’yu biliyorsa anlamı çözer.
Denotational semantics
Her sözdizimsel yapı için bir matematiksel fonksiyon tanımlanır. İkili sayı örneği:
M_bin('0') = 0
M_bin('1') = 1
M_bin(<bin_num> '0') = 2 * M_bin(<bin_num>)
M_bin(<bin_num> '1') = 2 * M_bin(<bin_num>) + 1
101 için: ‘1’ → 1, ‘10’ → 2, ‘101’ → 5. 10110 için 22. Programlarda durum (state) değişkenlerin değerlerinden oluşur; her ifade bir durumdan başka bir duruma giden fonksiyondur.
Axiomatic semantics
Bir ifade S için şu üçlüye Hoare triple denir:
P precondition (önkoşul), Q postcondition (sonkoşul). Anlamı: S çalışmadan önce P doğruysa, S bittiğinde Q doğrudur. Q verildiğinde, Q’yu garanti eden en az kısıtlayıcı önkoşula weakest precondition denir.
Atama aksiyomu
ataması ve postcondition Q için:
Q’daki her x yerine E yaz, sadeleştir. Örnek: x = 2 * y - 3 {x > 7}:
Kontrol etmeyi alışkanlık yap: y = 6 → x = 9 > 7 doğru; y = 5 → x = 7, 7 > 7 yanlış. Sınır doğru dışlanmış.
TUZAK · negatif katsayı.
a = -2 * b + 4 {a < 0}: . Eşitsizliğin iki tarafını -2’ye bölünce yön döner. b < 2 yazan yanar. Bir değer deneyerek kontrol et: b = 3 → a = -2 < 0, doğru.
Sıralı ifadeler
için önce ‘nin weakest precondition’ını bul, o ‘in postcondition’ı olur:
y = x + 4;
x = y * 2;
{x < 20}
adım 1 (son atama): 2y < 20 → y < 10
adım 2 (ilk atama): x + 4 < 10 → x < 6
sonuç: {x < 6}
Seçim
if B then S1 else S2 için:
if (x > 0) y = x - 1; else y = x + 1; {y > 0}: then dalı x > 1 verir (x > 0 ile birleşince yine x > 1). Else dalı x > -1 verir; x ≤ 0 ile birleşince tam sayılarda sadece x = 0. Cevap: x > 1 veya x = 0.
Daha güçlü önkoşul da geçerli
{x > 5} x = x - 3 {x > 0} geçerli bir üçlüdür, ama weakest değil. Weakest x > 3. Rule of consequence: P’ ⇒ P ise ve {P} S {Q} geçerliyse, {P’} S {Q} de geçerlidir. Yani weakest precondition’dan daha güçlü her önkoşul işe yarar; daha zayıfı yaramaz (x ≥ 3 için x = 3 → 0 > 0 yanlış).
Döngüler
while B do S için weakest precondition doğrudan hesaplanamaz; bir loop invariant (döngü değişmezi) I bulmak gerekir. I döngüden önce doğru olmalı, her turdan sonra da doğru kalmalı ve döngü bittiğinde () Q’yu gerektirmeli. Invariant bulmak tümevarıma benzer ve genelde vizede yüzeysel sorulur: “invariant nedir, hangi üç koşulu sağlamalı?”
Sık yapılan hatalar
| Yanlış | Doğrusu |
|---|---|
| wp’yi baştan sona hesaplamak | Sondaki postcondition’dan geriye git |
| -2’ye bölünce yönü korumak | Negatifle bölünce < ↔ > |
| Synthesized = aşağı akar | Synthesized yukarı, inherited aşağı |
| expected_type synthesized | Kardeşten geldiği için inherited |
| “Daha güçlü önkoşul geçersiz” | Geçerli, ama weakest değil |
| Static semantics çalışma zamanında | Derleme zamanında kontrol edilir |
| Attribute grammar = CFG | CFG + attribute + function + predicate |
| Denotational = makinede çalıştırmak | O operational; denotational matematiksel fonksiyon |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
Using the type-checking attribute grammar below, does the predicate hold for A = B + C when A is int, B is int and C is real?
1. <assign> -> <var> = <expr>
<expr>.expected_type <- <var>.actual_type
2. <expr> -> <var>[2] + <var>[3]
<expr>.actual_type <- if (<var>[2].actual_type = int) and
(<var>[3].actual_type = int) then int else real
predicate: <expr>.actual_type == <expr>.expected_type
3. <expr> -> <var>
<expr>.actual_type <- <var>.actual_type
predicate: <expr>.actual_type == <expr>.expected_type
4. <var> -> A | B | C
<var>.actual_type <- look-up(<var>.string)Cevabı göster
Sıra: önce yapraklar look-up ile tip alır (A int, B int, C real). Kural 1: expected_type ← A'nın tipi = int. Kural 2: iki operand da int değil → actual_type = real. Predicate: real == int → false. Derleyici bunu derleme zamanında tip hatası olarak bildirir.
Same grammar: does the predicate hold for A = B + C when A is real, B is int and C is int?
Cevabı göster
actual_type: iki operand da int → int. expected_type: A'nın tipi real. int == real değil → predicate false. Bu gramer coercion yapmaz, tipler birebir aynı olmalı. (A real, B real, C int durumunda ise actual = real, expected = real → true.)
In the grammar above, classify actual_type, expected_type, and the type that look-up returns for a <var> leaf.
Cevabı göster
Synthesized: bilgi çocuklardan ebeveyne, yukarı akar (<expr>.actual_type operandlardan hesaplanır). Inherited: bilgi ebeveynden ya da kardeşten aşağı/yana akar (<expr>.expected_type, kardeşi <var>'dan gelir). Intrinsic: yapraktaki değer dışarıdan, symbol table'dan gelir.
An attribute grammar for uses <S> -> <A> <B> <C>, where each of <A>, <B>, <C> has a synthesized attribute size and the predicate is A.size = B.size = C.size. Which strings pass both syntax and the predicate? aabbcc, aabbc, abc, aabcc, abca
Cevabı göster
CFG kısmı a+ b+ c+ biçimini kabul eder, sayıları predicate kontrol eder. aabbcc: 2,2,2 ✔. abc: 1,1,1 ✔. aabbc: 2,2,1 ✘ (predicate false). aabcc: 2,1,2 ✘. abca: biçim bozuk, syntax hatası. Bu dil context-free değildir; CFG tek başına üretemez, attribute grammar'ın gücü burada.
Compute the weakest precondition: x = 2 * y - 3 {x > 7}
Cevabı göster
Atama aksiyomu: postcondition'da x yerine 2y - 3 yaz. . Kontrol: y = 6 → x = 9 > 7 ✔; y = 5 → x = 7, 7 > 7 değil ✔ (sınır dışlandı).
Compute the weakest precondition of the sequence: y = x + 4; x = y * 2; {x < 20}
Cevabı göster
Sondan başa çalış. Son atama: . Bu, ilk atamanın postcondition'ı olur. İlk atama: . Kontrol: x = 5 → y = 9 → x = 18 < 20 ✔; x = 6 → y = 10 → x = 20 ✘.
Compute the weakest precondition: a = -2 * b + 4 {a < 0}
Cevabı göster
. Negatif sayıya bölünce eşitsizlik yön değiştirir: . Kontrol: b = 3 → a = -2 < 0 ✔; b = 2 → a = 0 ✘. En sık yapılan hata işaret çevirmeyi unutup b < 2 yazmak.
For integer x, compute the weakest precondition: if (x > 0) y = x - 1; else y = x + 1; {y > 0}
Cevabı göster
Seçim kuralı: . Then dalı: ; x > 0 ile birlikte x > 1. Else dalı: ; x ≤ 0 ile birlikte tam sayılarda sadece x = 0. Sonuç: x > 1 veya x = 0. (Gerçel sayılarda else kısmı olurdu.)
Which preconditions make the triple {P} x = x - 3 {x > 0} valid? (a) x > 0 (b) x > 3 (c) x > 5 (d) x >= 3
Cevabı göster
Weakest precondition x > 3. Geçerli olan her P bunu gerektirmeli (P ⇒ x > 3). x > 5 daha güçlü (stronger) ama geçerli. x > 0 ve x ≥ 3 geçersiz: x = 3 için sonuç 0, 0 > 0 değil. Weakest = geçerli olanların en az kısıtlayıcısı.
Using the denotational rules below, compute of 10110.
M_bin('0') = 0
M_bin('1') = 1
M_bin(<bin_num> '0') = 2 * M_bin(<bin_num>)
M_bin(<bin_num> '1') = 2 * M_bin(<bin_num>) + 1Cevabı göster
Soldan sağa birikir: '1' → 1; '10' → 2·1 = 2; '101' → 2·2 + 1 = 5; '1011' → 2·5 + 1 = 11; '10110' → 2·11 = 22. Denotational semantics her sözdizimsel yapıyı matematiksel bir nesneye (burada bir tam sayıya) eşler.
Name the dynamic semantics method described: (a) meaning is the sequence of state changes when the program runs on a virtual machine; (b) meaning is a mathematical function from states to states; (c) meaning is given by logical assertions before and after statements.
Cevabı göster
Operational: programı daha basit bir makine diline çevirip çalıştırmak gibi düşün, dil kılavuzları ve öğretimde kullanılır. Denotational: en titiz yöntem, matematiksel fonksiyonlar. Axiomatic: önkoşul/sonkoşul önermeleri, amaç program doğrulama (verification).
Compute the weakest precondition: b = a + 1; a = 3 * b; {a > 12}
Cevabı göster
Son atama: . İlk atama: . Dikkat: sondaki a ile baştaki a aynı değişken ama farklı anlardaki değerleri; sondan başa giderken her atama kendi değişkenini değiştirir.
Doğrulama: Attribute grammar tip kontrolleri, a^n b^n c^n predicate'i ve denotational ikili sayı değerleri node ile hesaplandı; her weakest precondition cevabı -50..50 aralığındaki tüm tam sayılarla programın kendisi çalıştırılarak kaba kuvvetle karşılaştırıldı (verify/SEN2001/03a.mjs).