סמנטיקה

חשוב שנדע להבדיל בין סינתקס של שפה לבין שמייצג את המבנה של השפה, סדר מילים וסימנים והרכבת משפטים שמהווים תוכנית מבחינת נכונות שלהם בסדר המילים.
הסמנטיקה היא המשמעות של אותם המילים, מה הן אומרות מבחינת התוכנית שלנו.

למשל נסתכל על הקוד הבא:

z:=x;x:=y;y:=z

מבחינת ניתוח סינתקס של התוכנית נוכל להבין שיש כאן 3 הצהרות (לפי ההפרדה של התו ;). כל אחת מהן מכילה משתנה בצד אחד עם אופרטור = ולאחר מכן מה שנראה כ expression שהוא בעצמו משתנה.

הסמנטיקה מתעסקת עם המשמעות הדקדוקית והנכונות הדקדוקית של תוכניות. כלומר, הסמנטיקה תבין שהמשמעות של התוכנית היא החלפת הערכים x,y תוך השמה של z להיות הערך הסופי של y.

ישנם מספר דרכים לפרמל את ההצהרה הנ״ל בגדול יש 3 דרכים עיקריות
- סמנטיקה תפעולית (Operational Semantic): המשמעות של מבנה מוגדרת על ידי החישוב שלו כאשר הוא מורץ על ידי מחשב.
- _סמנטיקה דנוטציונית משמעויות מעוצבות על ידי אובייקטים מתמטיים המייצגים את ההשפעה של ביצוע המבנים (למשל על ידי פונקציות).
- סמנטיקה אקסיומטית: מושפעת מ assertions. התוכנית יכולה להיות נכונה באופן חלקי אבל מוסיפים לה תכונות של preconditions ו postconditions כך שאם התוכנית מסתיימת אנחנו יודעים שהתנאים האלו התקיימו וקיבלנו תוצאה רצויה.

בשביל מה חשובה הסמנטיקה?

מעבר לפאן התאורטי שהיא נותנת לנו את היכולת לתת משמעות לתוכניות היא גם נותנת לנו כלי פרקטי לכתוב כלים שמפרשים שפות תכנות (interpreters)

שפת while

שפה אימפרטיבית פשוטה שעוזרת להדגים ולהסביר עקרונות סמנטיים כפי שציינו למעלה.
התחביר האבסטרקטי של while מחולק לקטגוריות הבאות
- Num- קבוצת המספרים השלמים
- Var- קבוצת המשתנים
- Aexp- קבוצת הביטויים האריתמטיים
- nNum:nAexp
- xVar:xAexp
- a1,a2Aexp:a1+a2,a1a2,a1a2Aexp
- Bexp- קבוצת המשתנים הבוליאנים נגדיר ביטוי בוליאני להיות b::=true|false|a1=a2|a1a2|¬b|b1b2
- Stm- קבוצת הפקודות
- אם xVar ו aAexp אז x:=aStm
- skipStm
- S1,S2Stm:S1;S2Stm
- if b then S1 else S2.
- while b do S
הערה: בזכות הסעיף השלישי Stm זאת קבוצת התוכניות.

Screenshot 2024-01-22 at 14.24.10.png
חשוב לשים לב שזהו סינתקס אבל את הסמנטיקה אנחנו יכולים לקבוע, למשל יכלנו לקבוע ש n::=0|1|n 0|n 1 כלומר n הוא מחרוזות המספרים הבינאריים. מגדירים זאת באמצעות פונקציות סמנטיות ובמקרה של מספרים N:NumZ.

AST

התחביר האבסטרקטי מתאר איך לבנות expressions בשפה מבלי לרדת לפרטים של keywords , הזחות וכו׳. מתחביר אבסטרקטי אפשר לבנות Abstract Syntax Trees שמייצג את המבנה הבסיסי ביותר של התוכנית. אפשר לבנות אותו מבלי לבדוק כל פרט שרלוונטי ל concrete syntax אם כי יכול להיות מצב שיווצרו לנו שתי AST מאותה תוכנית והסינתקס הקונקרטי יצטרך להכריע.

Pasted image 20240120235613.png

בגלל שלא נרצה לכתוב עצי סינתקס לכל תוכנית (ראו איך התוכנית הבסיסית הנ״ל יצרה עץ די גדול יחסית) נרצה לייצג אותם באמצעות תחביר ליניארי לשם כך נשתמש ב סוגריים. למשל

z:=x;(x:=y;y:=z)

יבטא את העץ השמאלי בתמונה ו

(z:=x;x:=y);y:=z

יבטא את העץ הימני.

לדוגמה, נבנה עת סינתקס עבור התוכנית הבאה:

y:=1;
while not(x=1) do (
	y:=y*x;
	x:=x-1;
)

Screenshot 2024-01-21 at 16.37.13.png|450

Semantic Functions

בסופו של דבר הסינתקס האבסטרקטי מורכב מexpressions אבל המשמעות של ביטוי כזה נקבע לפי הערך של המשתנים שנמצאים בתוך הביטוי הזה.

לשם כך נגדיר states שזאת תהיה פונקציה ממשתנה לערך שלו

State=VarNum

אם s הוא מצב ו x הוא משתנה נסמן את s(x) כ s x . נשים לב שבגלל שאנחנו מגדירים את זה כפונקציה אנחנו בהכרח כופים על כל משתנה לקבל ערך אחד מהגדרה ח״ע.

סמנטיקה של ביטויים אריתמטיים:
כעת, בהינתן ביטוי אריתמטי a ומצב s נוכל כעת לקבוע את הערך של הביטוי.
נגדיר פונקציה שלמה A:Aexp(StateNum) באופן הבא:

a) nNum:A[n]s=n
b) xVar:A[x]s=s x
c) A[a1+a2]s=A[a1]s+A[a2]s
d) כפל וחיסור דומה לסעיף הקודם.

נשים לב להבדלה בין הסימנים בצד שמאל ולסימנים האריתמטיים בצד ימין. צד שמאל הם סימנים של חלק מהסינתקס האבסטרקטי ובצד ימין זה ביטויים אריתמטיים כפי שאנחנו מכירים.

דוגמה:
נניח שיש מצב x המקיים s x=3 . נחשב את A[x+1]s. לפי הכללים הנ״ל מתקיים A[x+1]s=A[x]s+A[1]s=s(x)+1=3+1=4 .

סמנטיקה של ביטויים בוליאנים:
נגדיר אותה כפונקציה B:Bexp(s{tt,ff}) על ידי:
a) B[false]s=ff
b) B[true]s=tt
c) B[a1=a2]s={ttA[a1]s=A[a2]sffelse
d) B[a1a2]s={ttA[a1]sA[a2]sffelse
e) B[¬b]s={ttB[b]s=ffffelse
f) B[b1b2]s={ttB[b1]s=tt and B[b2]s=ttffelse

Natural Semantic

סמנטיקה טבעית היא אחת הדרכים לתאר סמנטיקה של פקודות. בסמנטיקה של פעולות אנחנו מתעניינים בשינויי המצבים במהלך הרצה של תוכנית. הסמנטיקה הטבעית שמה בחשיבות עליונה את התוצאה הסופית של הריצה של התוכנית שלנו ואיך תוצאה זאת מושגת.

המטרה של statements ב while זה לשנות את הstates. למשל אם x=3 במצב s ואנחנו מריצים את הפקודה x:=x+1 אז אנחנו מקבלים מצב חדש שבו x=4 . לכן, בעוד שסמנטיקה של ביטויים אריתמטיים ובוליאניים רק משקיפה על הstate ומאפשרת לנו לחשב אותו , הסמנטיקה של פעולות גם תשנה את הstate הזה. בסמנטיקה תפעולית (ובפרט גם טבעית) החשיבות היא גם על איך התוכנית רצה ולא רק התוצאות של הריצה. ליתר דיוק, העניין הוא סביב כיצד המצבים משתנים במהלך ריצת התוכנית.

בסמנטיקה טבעית מתעניינים בקשר בין המצב ההתחלתי של תוכנית למצב הסופי שלה.

נגדיר כמה כללים של סמנטיקה של פקודות (Operational Semantic)
יהי s מצב , y משתנה ו v מספר, המצב s[yv] יוגדר על ידי :

s[yv]x={vx=ys xelse

המשמעות הסמנטית היא כאשר המשתנה y ממופה לערך v מה יהיה הפלט של הפונקציה s x

זה מבוסס על עקרון ההחלפה. זה בעצם s רק ש y כבול לערך v.

Pasted image 20240122003944.png

הסמנטיקה התפעולית מבוססת על היגדים מהצורה S,ss האינטואיציה של זה היא שכאשר אנחנו במצב s מפעילים את הפקודה S ועוברים למצב s .

נגדיר מספר כללי היסק עבור היחס :

Pasted image 20240122010109.png

נוכל להוסיף כללים חדשים לסמנטיקה שלנו, למשל נגדיר if(x:=e) then S1 else S2 ככלל חדש ונוכל לכלול אותו בסמנטיקה שלנו כך :

Pasted image 20240122144829.png

נוסיף כלל נוסף גם case(b1:S1),(b2:S2),(else:S3)

Screenshot 2024-01-22 at 14.49.27.png

לחוק (כפי שרשום למעלה) יש את התצורה הכללית :
Pasted image 20240122010235.png
כאשר S1,Sn הם מרכיבים מידיים של S או שהם פקודות שנוצרות מרכיבים מידיים של S.
לכלל יש הנחות (מה שרשום מעל הקו) ומסקנה אחת (מתחת לקו) כמו כן יכול להיות לכללים גם תנאים בוליאנים. אפשר להסתכל על החלק העליון כגזירה של החלק התחתון. בעצם  if, while, comp הם כללי גזירה ו ass, skip הם אקסיומות.

כללים ללא הנחות נקראים אקסיומות למשל ass .

עצי גזירה

נגדיר את עץ הגזירה של S,ss הוא עץ ששורשו הוא S,ss העלים הם אקסיומות וכל קודקוד הוא מסקנה מילדיו לפי אחד מכללי הגזירה.

עצים גזירה מציירים כאשר השורש למטה.

הגדרה: ההיגד S,ss מתקיים אם יש עץ גזירה ששורשו הוא S,ss.
נסתכל על הדוגמה הבאה:

יהי s0 מצב כך ש s0x=5 ו s0y=7 .נסמן s1:s0[z5],s2:s1[x7],s3:s2[y5] נסתכל על הפקודה הבאה

(z:=x;x:=y);y:=z,s0

נרצה להראות שזאת תוביל אותנו ל s3 .
נבנה עץ גזירה

Pasted image 20240122125327.png

משפט: אם <S,s>s אז עבור s מתקיים שאם <S,s>s אז s=s .

הוכחה:
אפשר לעשות את זה ב אינדוקציה על מבנה עץ הגזירה של <S,s>s .

בסיס: נבדוק על כל אחת מהאקסיומות.
a) אם הגזירה הנ״ל מובילה אותנו ל skip לאחר הפעלה יחידה, זה אומר ש S=skip ולכן זה אומר s=s. כיוון ש S=skip אז אם נניח <S,s>s נקבל <skip,s>s=s=s כדרוש.

b) אם <S,s>s נגזר על ידי עץ הגזירה עם הפעלה יחידה של כלל ass. במקרה זה S חייבת להיות מהצורה x:=a ולכן מתקיים s=s[xA[a]s]. כעת אם נניח ש <S,s>s נקבל באותה צורה s=s[xA[a]s]=s כדרוש.

נשים לב ש skip ו whileff דומים זה יוצא אותה הוכחה.

צעד 1:
ל <S,S>s יש עץ גזירה שבו הכלל האחרון שהופעל הוא Comp אז העץ הוא

<S1,s>s0  <S2,s0>s<S1;S2,s>s

עבור s0 כלשהו.

כלומר קיימים S1;S2 כך ש S=S1;S2

כעת נניח ש <S,s>s עץ הגזירה ייראה כך

<S1,s>s1  <S2,s1>s<S1;S2,s>s

עבור s1 כלשהו.

מהנחת האינדוקציה נוכל להסיק ש כיוון ש <S1,s>s1 וגם <S1,s>s0 אז s0=s1 ולכן <S2,s1,0>s=s כדרוש.

נשים לב שההנחה היא רק על עץ הגזירה הראשונה ולא השני.
יתר הכללים עובדים בצורה דומה.

בגדול המשמעות של האינדוקציה היא לסקור את כל המבנים האפשריים החל מהאקסיומות ועד לכללים, אם נראה על כל האקסיומות והכללים הבסיסיים שמפורטים בטבלה למעלה שהמשפט מתקיים, אז הוא כמובן מתקיים על כל צירוף שלהם.

לסיכום:
כדי להוכיח שמתקיימת תכונה מסוימת על סמנטיקה נשתמש באינדוקציה על צורת העץ כאשר הבסיס הוא התכונה המתקימת על האקסיומות ובצעד האינדוקציה לכל כלל גזירה נניח שמתקיימת התכונה ונוכיח למסקנה של הגזירה שהתכונה מתקיימת.

הוכחת שקילות סמנטית

נרצה להראות שקילות סמנטית בין שני קטעי קוד בשפת while
while b do S ~ if b then (S; while b do S) else skip

לשם כך נצטרך להוכיח את שתי הטענות הבאות :

Screenshot 2024-01-22 at 19.23.39.png

מזכיר הוכחה של שיוויון על ידי הכלה דו כיוונית. נרצה להראות שאם אחד נגזר למצב s אז גם השני נגזר לאותו מצב.

נעשה כיוון אחד, הכיוון השני דומה. מתקיים אם כן, <while b do S,s>s אזי, ישנו עץ גזירה T שגוזר את הכלל הזה. נחלק למקרים:

a) אם B[b]s=tt במקרה זה עץ הגזירה נראה מהצורה

Screenshot 2024-01-22 at 19.29.01.png

כאשר T1=<S,s>s ו T2 הוא מהצורה <while b do S, s>s.

נוכל לבצע comp על T1,T2 ונקבל :

Screenshot 2024-01-22 at 19.31.38.png

כעת בגלל שb=tt נוכל להשתמש בכלל iftt ונקבל

Screenshot 2024-01-22 at 19.33.16.png

שמנו skip כי אין לו חשיבות כיוון ש b הוא תמיד true.

b) כאשר B[b]=ff העץ גזירה הוא מהצורה <while b do S, s>s  
במצב זה אנחנו נשארים באותו מצב ולכן נגדיר s=s . נוכל לנצל זאת כדי להשתמש ב <skip, s>s ונשתמש בזה ובכלל ifff כדי לגזור מהכלל הזה את <if b then S1 else S2,s>s כדי לקבל

Screenshot 2024-01-22 at 19.39.27.pngֿ