Πολλές φορές έχουμε συζητήσει τις κατηγορίες λαθών στο Στέκι, αλλά δεν έχουμε καταλήξει. Ξεκινάω μια προσπάθεια εμβάθυνσης, ώστε να συμφωνήσουμε τουλάχιστον στο επιστημονικό κομμάτι, και στη συνέχεια να προσπαθήσουμε να επεκταθούμε στην ερμηνεία του βιβλίου, ποια σημεία θεωρούνται γκρίζα, πώς θα πρέπει να τα διδάσκουμε/εξετάζουμε κλπ.
Θα ξεχωρίσω πρώτα τις κατηγορίες λαθών με βάση τα παρακάτω εργαλεία:
- Οι
lexers κάνουν λεκτικό έλεγχο. Ανοικτά αλφαριθμητικά, άκυρα σύμβολα κλπ ανιχνεύονται εδώ. Για απλούστευση, στη ΓΛΩΣΣΑ αυτά τα έχουμε ενώσει με τα συντακτικά λάθη.
- Οι
parsers κάνουν συντακτικό έλεγχο. "ΓΙΑ ι ΕΠΑΝΑΛΑΒΕ" ανιχνεύεται εδώ.
- Οι
linters κάνουν στατική ανάλυση του κώδικα, με σκοπό να ανιχνεύσουν λογικά λάθη πριν ακόμα εκτελεστεί το πρόγραμμα. ΑΝ λοιπόν θεωρήσουμε ότι η ΓΛΩΣΣΑ περιλαμβάνει linter, η διαίρεση με το μηδέν, η προσπέλαση στοιχείων εκτός πίνακα κλπ θα μπορούσαν να ανιχνευτούν και πριν την εκτέλεση, ενώ κανονικά ανιχνεύονται κατά την εκτέλεση.
- Οι βιβλιοθήκες των γλωσσών προγραμματισμού και των λειτουργικών συστημάτων (
runtime systems) εγείρουν τα λάθη χρόνου εκτέλεσης, και εμφανίζουν είτε warnings είτε προκαλούν αντικανονικό τερματισμό του προγράμματος, πριν το φυσιολογικό ΤΕΛΟΣ_ΠΡΟΓΡΑΜΜΑΤΟΣ.
Πάμε τώρα στους compilers / interpreters. Αυτοί στην "απλή" τους έκδοση περιλαμβάνουν μόνο lexers / parsers / runtime-systems.
Άρα αν ζητήσουμε η ΓΛΩΣΣΑ να υλοποιείται με "απλό" Διερμηνευτή, τότε τα περισσότερα "ασαφή σημεία" που συζητάμε, όπως η διαίρεση με το μηδέν ή η ΓΡΑΨΕ εντός συναρτήσεων, ΔΕΝ μπορούν να ανιχνευτούν πριν την εκτέλεση.
Όμως, όλοι οι μοντέρνοι compilers / interpreters είναι πολύ προχωρημένοι και περιλαμβάνουν και linters ή/και code optimizers. Σε αυτήν την περίπτωση, πολλά λάθη θα μπορούσαν να ανιχνευτούν και πριν την εκτέλεση. Όμως ΔΕΝ θα έπρεπε να τα αποκαλούμε "συντακτικά λάθη" για να μην μπλεχτούμε, αλλά να κάνουμε μια νέα κατηγορία λαθών γι' αυτά, "compiler warnings" ή "lintian λάθη" κλπ.
Για παράδειγμα, ο
GNU compiler gcc περιλαμβάνει τις παρακάτω ενδιαφέρουσες επιλογές:
- -fsyntax-only: μόνο απλός συντακτικός έλεγχος, καθόλου linting
- -Wfatal-errors: τα warnings γίνονται λάθη και δεν προχωράνε σε εκτέλεση
- -Wunused-variable: μεταβλητή δηλώθηκε αλλά δεν χρησιμοποιήθηκε
- -Wuninitialized: μεταβλητή χρησιμοποιήθηκε χωρίς να αρχικοποιηθεί
- -Warray-bounds: προσπέλαση στοιχείου πίνακα εκτός ορίων
- -Wdiv-by-zero: προειδοποίηση για στατική διαίρεση με το μηδέν
Έτσι, υπάρχει η δυνατότητα το 1/0 να μην το καταλάβει καθόλου, ή να το καταλάβει και να θεωρηθεί fatal warning και να μην προχωρήσει σε εκτέλεση. ΔΕΝ υπάρχει όμως η δυνατότητα να καταλάβει το ΔΙΑΒΑΣΕ α, ΓΡΑΨΕ 1/α, και ο χρήστης να δώσει α=0, αυτό θα είναι πάντα σφάλμα χρόνου εκτέλεσης. Υπάρχει και η δυνατότητα μια μεταβλητή που δεν χρησιμοποιήθηκε να θεωρηθεί σφάλμα και να μην γίνεται εκτέλεση πριν διορθωθεί.
Προτείνω να αναφέρουμε ως συντακτικά λάθη, και να εξετάζουμε ως συντακτικά λάθη, μόνο αυτά που αντιστοιχούν στο "-fsyntax-only". Δηλαδή κανείς μας να μην αναφέρει το 1/0 ως συντακτικό λάθος.
Περαιτέρω, αν ζητήσουμε από το Διερμηνευτή να κάνει έλεγχο τύπου lint, και να ανιχνεύσει τέτοια λάθη πριν την εκτέλεση, ΠΑΛΙ να μην τα αναφέρουμε ως συντακτικά.
Για παράδειγμα, ο Διερμηνευτής κάνει κάποιους ελέγχους lint, π.χ. έχει επιλογή για το αν επιτρέπεται ή όχι η ΓΡΑΨΕ μέσα σε συναρτήσεις. Αυτό λοιπόν ανιχνεύεται πριν την εκτέλεση, άρα κάποιος θα μπορούσε να το πει "συντακτικό λάθος". Δεν παραβιάζεται όμως κάποιος συντακτικός κανόνας, αυτό το λάθος ανιχνεύεται κατά το linting / static code analysis, που είναι διαφορετικό πράγμα. Δεν έχει νόημα ούτε εμείς ούτε οι μαθητές να μπερδευτούμε με αυτό. Βλέποντας όλη τη σύγχιση που έχει δημιουργηθεί, ίσως θα ήταν καλύτερα να μην είχα βάλει καθόλου linting στο Διερμηνευτή.
Ένα ακόμα σημείο αντιπαραθέσεων είναι τα λάθη χρόνου εκτέλεσης, που είναι ταυτόσημα με τα "λάθη που προκαλούν αντικανονικό τερματισμό", αλλά δεν αναλύονται και πολύ καλά. Λάθη χρόνου εκτέλεσης λέμε τα warnings ή errors που εμφανίζει το runtime system. Η διαίρεση με το μηδέν θα προκαλέσει CPU interrupt το οποίο θα το χειριστεί η libc κλπ. Και μπορούν είτε να εμφανίσουν ένα warning "διαίρεση με το μηδέν, οκ, συνεχίζω", ή να σταματήσουν με "αντικανονικό τερματισμό" την εκτέλεση, είτε ακόμα να πάνε στο "onerror ... goto" που λέει το βιβλίο για την Basic, όπου γίνεται ο "χειρισμός του λάθους χρόνου εκτέλεσης", και έτσι αποφεύγεται ο αντικανονικός τερματισμός.
Εδώ για απλούστευση θα πρότεινα όλα τα λάθη χρόνου εκτέλεσης να θεωρήσουμε ότι προκαλούν τερματισμό και είναι εντελώς ισοδύναμα με τα "λάθη που προκαλούν αντικανονικό τερματισμό".
Προσοχή, ένα άπειρο loop δεν προκαλεί κανένα λάθος χρόνου εκτέλεσης, ούτε η CPU ούτε η libc κλπ δεν καταλαβαίνουν ότι κάτι πάει στραβά. Ο Διερμηνευτής εδώ πάλι πάει να κάνει τον έξυπνο και λέει "έχουν γίνει 100.000 επαναλήψεις, μήπως υπάρχει άπειρο loop;" αλλά δεν μπορούμε αυτό να το εντάξουμε στα λάθη εκτέλεσης.
Τελευταίο σημείο αντιπαραθέσεων είναι τα λογικά λάθη. Λογικά λάθη είναι όλα τα λάθη που έχουμε στον κώδικά μας, που αν μας τα επισημάνει κάποιος, θα σπεύσουμε να τα διορθώσουμε. Κανένα λογικό λάθος δεν πιάνεται κατά τον συντακτικό έλεγχο. Μερικά λογικά λάθη πιάνονται κατά τον lint έλεγχο. Μερικά λογικά λάθη προκαλούν λάθη χρόνου εκτέλεσης, ενώ άλλα όχι, οπότε τα παρατηρούμε μόνο επειδή το πρόγραμμα μερικές φορές δεν κάνει αυτά που θέλαμε.
Έτσι, παραθέτω εδώ μερικές "ζόρικες" ερωτήσεις/απαντήσεις, κι αν θέλετε ρωτήστε κι άλλες, να τις συγκεντρώσω εδώ, και από δίπλα μετά να γράψουμε αν η πλειοψηφία συμφωνεί ή όχι.
Τι λάθος είναι το 1/0;
Κανονικά χρόνου εκτέλεσης, αλλά μπορεί να βρεθεί και με static code analysis (lint). Δεν μπορεί όμως να θεωρηθεί συντακτικό.
Υπάρχουν λογικά λάθη που να μην προκαλούν λάθη χρόνου εκτέλεσης;
Ναι, για παράδειγμα ένα άπειρο loop.
Υπάρχουν λάθη χρόνου εκτέλεσης που να μην είναι λογικά;
Ναι, για παράδειγμα ΓΡΑΨΕ "Hello world" να βγάζει "σφάλμα, δεν βρέθηκε οθόνη (stdout)". Η επίλυσή του δεν αφορά τον κώδικά μας άρα δεν είναι λογικό λάθος (μας).
Τι λάθος είναι η ΓΡΑΨΕ εντός συναρτήσεων;
Με βάση τις γενικότερες επιστημονικές μας γνώσεις, θα θέλαμε να μην είναι λάθος. Με βάση το βιβλίο, είναι απλά κακή πρακτική. Με βάση την οδηγία του Υπουργείου, είναι λάθος χωρίς να αναφέρεται ο χρόνος που θα ανιχνευτεί. Με βάση το Διερμηνευτή, ΑΝ η σχετική επιλογή είναι ενεργοποιημένη (που είναι by default), είναι λάθος τύπου lint/static code analysis. Δεν υπάρχει κάποια ένδειξη πουθενά ώστε να θεωρηθεί συντακτικό ή χρόνου εκτέλεσης. Άρα στα πλαίσια της ΑΕΠΠ αυτή η ερώτηση είναι βαθύ γκρίζο.