Τετάρτη 11 Μαΐου 2016

Πώς δουλεύει ο διερμηνευτής της Μ2000 Μέρος Α.

Η τρέχουσα έκδοση της Μ2000 είναι η 11. Το κείμενο εδώ γράφτηκε πριν από αρκετά χρόνια (έκδοση 8). Ακόμα και η 11η έκδοση λειτουργεί με άμεση εκτέλεση κατά τη μετάφραση. Εσωτερικά έχει Hash Tables όπου οι αναζητήσεις γίνονται σε O(1), και η επιλογή της συνάρτησης (μεθόδου) που καλεί η λέξη γίνεται με χρήση δείκτη στη συνάρτηση, οπότε δεν έχουμε διπλή αναζήτηση, μια αν υπάρχει η λέξη και μια άλλη να βρούμε στις υπάρχουσες ποια συνάρτηση δίνει. Σε πολλές περιπτώσεις υπάρχει look ahead, όπως στις εκφράσεις. Έτσι αν θέλουμε αριθμητική έκφραση θα γίνει αναζήτηση για το είδος της έκφρασης μόνο συντακτικά (χωρίς να μας ενδιαφέρει αν έχουμε σωστά/γνωστά αναγνωριστικά) και αν είναι σωστό τότε θα προχωρήσει ο μεταφραστής σε εκτέλεση. Ο διορθωτής προγραμμάτων (ενσωματωμένος στο περιβάλλον) ελέγχει αν έχουμε κλειστά αλφαριθμητικά και κλειστές αγκύλες, την στιγμή που τον κλείνουμε (με Esc) και μας λέει το πρόβλημα και αν θέλουμε όντως να βγούμε ή να επιστρέψουμε. Πολλές δομές γράφονται και χωρίς αγκύλες.

Ο μεταφραστής της Μ2000 είναι μια συνάρτηση που καλεί άλλες συναρτήσεις, και κάποιες από αυτές καλούν ξανά τον μεταφραστή, μέχρι που να τελειώσει το πρόγραμμα. Υπάρχει ένας Task Manager, που μπορεί να βάλει νήματα (εκτελέσεις του μεταφραστή) σε αναμονή, και να διαχειριστεί τις εκτελέσεις τους. Αυτό γίνεται με έναν χρονιστή των Windows, που δίνει tick στον Task Manager για να αποφασίζει αν θα εκτελεστεί άλλο task και πότε! Τα νήματα δουλεύουν είτε το ένα μετά το άλλο ανά διάρκεια νέας εκκίνησης που έχει οριστεί για το καθένα, ή μπορούμε να επιλέξουμε την γρήγορη εναλλαγή εκτέλεσης νημάτων σε επίπεδο εντολής. Εκτός από νήματα μπορούμε να διαχειριστούμε γεγονότα, από διάφορες πηγές. Το περιβάλλον ενσωματώνει GUI με φόρμες και στοιχεία ελέγχου που προγραμματίζουμε με απλές εντολές. Μπορούμε ταυτόχρονα να έχουμε και φόρμες (παράθυρα) και νήματα. Τέλος μπορούμε να συνδέσουμε εξωτερικά dll (συναρτήσεις) και αντικείμενα τύπου COM με ή χωρίς γεγονότα. Ο Task Manager εκτός από τα νήματα λειτουργεί τα μηνύματα MIDI (για 16 φωνές) και ουρές για επικοινωνίες με named pipes (μπορούμε να συνδέσουμε πολλά περιβάλλοντα Μ2000, που τρέχουν σε έναν υπολογιστή, ή ένα τοπικό δίκτυο, μέσω named pipes).
Το περιβάλλον έχει ενσωματωμένα αντικείμενα για διαχείριση εξωτερικών προγραμμάτων μέσω stdin stdout, καθώς και επικοινωνία για σειριακές πόρτες.
Δεν χρειάζονται εγκαταστάσεις βιβλιοθηκών γιατί οτιδήποτε χρησιμοποιεί το περιβάλλον είτε είναι ενσωματωμένο στο κώδικά του είτε το έχει μόνιμα το σύστημα των Windows.


Παλαιό κείμενο:

Θα ακολουθήσουν μερικές αναρτήσεις  για το πώς δουλεύει η Μ2000 εσωτερικά, πως δουλεύει ο διερμηνευτής, πως αποθηκεύονται οι μεταβλητές, οι πίνακες, τα αντικείμενα, γενικά όλα τα μέρη της γλώσσας μέσα από το πρόγραμμα που έχει γραφτεί σε Visual Basic 6.

Στη σημερινή ανάρτηση θα δούμε βασικά στοιχεία. Ο τρόπος λειτουργίας της Μ2000 είναι απλός, αλλά αν ψάξει κανείς τον κώδικα θα δει πολλές λεπτομέρειες που δυσκολεύουν την ανάγνωση. Η ιδέα παραμένει απλή!
Ο διερμηνευτής εκτελεί κώδικα καθώς τον διαβάζει, ή με το λεκτικό της Μ2000 καθώς τον καταναλώνει. Αν μιλάγαμε για μεταφραστή θα είχαμε μια λειτουργία μετάφρασης και μια εκτέλεσης. Ο κώδικας που θα εκτελούσαμε θα υπήρχε πριν την εκτέλεση οπότε η έννοια της κατανάλωσης εκεί δεν ισχύει. Στη Μ2000 ο διερμηνευτής δεν έχει διαφορετική λειτουργία για μετάφραση και εκτέλεση. Όταν καλούμε ένα τμήμα ο πηγαίος κώδικας φορτώνεται και εκτελείται. Σε αντίθεση αυτού, σε έναν μεταφραστή (compiler) όλες οι διαδικασίες έχουν φορτωθεί ή φορτώνονται δυναμικά, ως εκτελέσιμος κώδικας χωρίς μετάφραση. Στη Μ2000 δεν κάνουμε κάτι τέτοιο, κάθε φορά εκτελούμε πηγαίο κώδικα, ο οποίος φορτώνεται σε ένα αντικείμενο εκτέλεσης και τελικά εκεί καταναλώνεται, δηλαδή κάθε φορά που εκτελείται μια εντολή, αυτή αφαιρείται από το κώδικα. Αυτό δεν είναι μειονέκτημα, γιατί υπάρχουν συχνά μπλοκ επεξεργασίας με αγκύλες { } και αυτές εκτελούνται με αντίγραφο. Δηλαδή μόλις μπει  ο διερμηνευτής σε ένα μπλοκ με αγκύλες, τότε δημιουργεί μια νέα κόπια του και τρέχει το κομμάτι αυτό. Κάθε κόπια, είναι στην ουσία μια συνάρτηση εκτέλεσης κώδικα που επιστρέφει ότι απέμεινε από τον κώδικα και κάποιες σημαίες, που δηλώνουν τι πρέπει να γίνει μετά.

Μια βασική συνάρτηση εκτέλεσης στο κώδικα του διερμηνευτή της Μ2000 είναι η Execute, (γραμμένη σε Visual Basic 6). Στη Vb6 ότι δεν αναφέρεται by value ή δεν είναι σε παρενθέσεις είναι πέρασμα by reference.

VB6 code
Function Execute(bstack As basetask, b$, Once As Boolean, Optional linebyline As Boolean, Optional loopthis As Boolean = False) As Long

Παρατηρούμε ότι η Execute γυρνάει ένα ακέραιο, ότι δέχεται ένα αντικείμενο τύπου basetask, καθώς και τρεις σημαίες, οι δυο είναι Optional, δηλαδή ενδέχεται να κληθεί η συνάρτηση χωρίς να δοθούν ειδικά τιμές, και η loopthis έχει εξορισμού την τιμή False.

Ο κώδικας που θα εκτελεστεί είναι το b$ και στην επιστροφή θα έχει αλλάξει. Δηλαδή μπορεί να έχει μήνυμα ή κώδικα. Αυτό καθορίζεται από τον αριθμό που επιστρέφει η Execute.

Εσωτερικά η execute διαχειρίζεται τις δομές επανάληψης, και βρίσκει αν μια εντολή είναι αναγνωριστικό και καλεί την εντολή (με έναν από τους δυο τρόπους, απευθείας με δείκτη σε συνάρτηση ή με κλήση της Identifier. Παρατηρούμε ότι η Identifier δέχεται και αυτή το αντικείμενο εκτέλεσης, το what$ που είναι η εντολή που θα εκτελέσει,  το rest$ που είναι ο κώδικας από το  b$ του Execute (ότι έχει απομείνει), μετά δυο ακόμα η nocom που αν είναι True λέει στην Identifier να μην το ψάξει, έχει κλήση σε τμήμα χρήστη (δεν υπάρχει στα selecet case) και το Lang που του λέει σε τι γλώσσα είναι εντολή (αγγλικά είναι το 1 και ελληνικά το 0), και αυτό έχει βρεθεί πιο πριν.
(παλαιότερα μια Print θα βρίσκονταν στο κώδικα, και θα έμπαινε στην identifier για να εκτελεστεί. Στην 8.1 έκδοση αυτό δεν συμβαίνει άμεσα, αλλά αν δώσουμε @Print τότε θα συμβεί! Αυτό γίνεται γιατί μπορούμε να φτιάξουμε ένα τμήμα με το όνομα Print, και το πετυχαίνει ο διερμηνευτής, αλλάζοντας το όνομα της Print στον εσωτερικό πίνακα αναζήτησης! Έτσι η Print δεν θα βρεθεί στις εντολές και θα αναζητηθεί στα τμήματα:
            Τμήμα Γενικό Τύπωσε {
                  @Τύπωσε "οκ"
            }
            Τύπωσε
            @Τύπωσε "όλα καλά"

Ορισμένες εντολές δεν έχουν περάσει στην αυτόματη κλήση (θα δούμε πώς γίνεται αυτή), και χρησιμοποιείται ο "παλιός" τρόπος. (σε μετρήσεις που έχω κάνει ο παλιός τρόπος δεν είναι πιο αργός. Αν και ο "γρήγορος" βρίσκει το όνομα και άμεσα την διεύθυνση της ρουτίνας που θα καλέσει -μέσω μιας συνάρτησης μεσολάβησης, ή proxy στην ορολογία των υπολογιστών, η κλήση έχει ένα πέναλτι λόγω αλλαγής της cache μνήμης του επεξεργαστή, για να μεταβεί σε άλλη περιοχή κώδικα. Η αναζήτηση στο μακρύ "select case" γίνεται στην ίδια περιοχή κώδικα. Παρόλα αυτά η "γρήγορη" μέθοδος σε κάποιους επεξεργαστές με λιγότερο εκμεταλλεύσιμη cache μνήμη θα κερδίζει.(ή πολύ μικρότερη ή πολύ μεγαλύτερη από ένα όριο, θα δίνει κέρδος, και αυτό συμβαίνει γιατί στην πολύ μεγάλη θα είναι μέσα στη cache μνήμη και η ρουτίνα, που βρίσκεται μακριά και κάνει αρχικά τη ζημιά, και στην άλλη περίπτωση, στη πολύ μικρή cache, θα έχουμε τμηματική παραλαβή του select case, άρα χάσιμο του όποιου πλεονεκτήματος). Η γρήγορη εκδοχή, θα είναι γρήγορη ακόμα και με μικρή cache (μιλάμε για την ελεύθερη, όχι κατ' ανάγκη για το σύνολό της).


VB6 code
Function Identifier(basestack As basetask, what$, rest$, Optional nocom As Boolean = False, Optional lang As Long = 1) As Boolean


Η Execute διαχειρίζεται και τις εντολές καταχώρησης-ανάθεσης σε μεταβλητή. Όταν διαβάζει ένα όνομα βρίσκει τι γενικός τύπος είναι, μέσω μια συνάρτησης που καλεί απευθείας με μια Select Case
Υπάρχουν αρκετά νούμερα που μπορεί να γυρίσει, επιπλέον γυρνάει στο w$ αυτό που μπορεί να βρήκε σε κεφαλαία γράμματα (ενδέχεται να μην βρει κάτι, γιατί είναι ένας χαρακτήρας στην αρχή μη αναγνωρίσιμος), στο ss$ γυρνάει ότι βρήκε χωρίς αλλαγές (χρησιμοποιείται ορισμένες φορές όπως στην κλήση ρουτινών, το όνομα όπως γράφτηκε, δηλαδή να ξεχωρίζουν πεζά με κεφαλαία και με γράμματα με τόνους) και τέλος η γλώσσα (μας δίνει σε τι γλώσσα είναι η εντολή, ελληνικά ή αγγλικά) και έχει σημασία γιατί ορισμένες εντολές δέχονται επιπλέον αναγνωριστικά και ζητάνε μόνο αυτά με την ίδια γλώσσα με την εντολή που χρησιμοποιήθηκε.

VB6 code
Select Case IsLabelDotSub(HERE$, b$, w$, ss$, lang)
...(επεξεργασία)

Δεν έγραψα παραπάνω για την HERE$. Αυτή είναι μια γενική μεταβλητή. Έτσι ξεκίνησε από την πρώτη έκδοση και παραμένει μέχρι και την έκδοση 8. Αυτό που γράφεται σε αυτήν είναι το NameSpace δηλαδή το όνομα χώρου όπου δημιουργούνται μεταβλητές. Αν δημιουργήσουμε μεταβλητή στην εισαγωγή εντολών (στο ">_" που βγαίνει στην κονσόλα), τότε αφενός θα κληθεί η Interpreter μια light έκδοση του Execute, με Here$="" δηλαδή το γενικό ή global. Αν φτιάξω την Α τότε θα έχω στη λίστα μεταβλητών την Α. Αν όμως καλέσω ένα τμήμα Α και φτιάξω εκεί την Α θα έχω την Α.Α. Η Μ2000 δεν φτιάχνει τεράστια ονόματα για όνομα χώρου. Έχει έναν τρόπο περιορισμού τους. Όμως σε ορισμένες κλήσεις που έχουμε κλήση σε ιδιότητα αντικειμένου το όνομα της ιδιότητας μπορεί να έχει τρία συστατικά, το όνομα τμήματος, το όνομα αντικειμένου, και αυτό της ιδιότητας. Ουσιαστικά αυτό που γίνεται είναι ότι το HERE$ κρατάει τα δυο πρώτα και όταν τρέχει ο κώδικας κάθε μεταβλητή αναζητείται πρώτα στο HERE$+"."+όνομα$ ή αν έχει μια τελεία στο HERE$+όνομα$, ή αν έχει πολλές τελείες στην αρχή (γίνεται και αυτό) τότε ψάχνει την αντιστοιχία σε εσωτερική λίστα στο αντικείμενο εκτέλεσης τύπου Basetask.


Για το αντικείμενο εκτέλεσης (κλάση Basetask)
Το αντικείμενο εκτέλεσης στις πρώτες εκδόσεις της γλώσσας ήταν το αντικείμενο εισόδου/εξόδου (π,χ, κονσόλα) ή μόνο εξόδου (π.χ. εκτυπωτής). H Execute και η Identifier όταν εκτελούνται "γνωρίζουν" εκτός από το κώδικα και το που τρέχουν, με τι είσοδο και έξοδο έχουν να κάνουν.

Αυτά ακριβώς, η είσοδος- έξοδος, ο κώδικας και η επιστροφή κατάστασης είναι βάση της Μ2000 και κάθε γλώσσας. Η Μ2000 μπορεί να τα αλλάζει πολύ εύκολα με μπλοκ που κάνουν την αλλαγή. Π.χ.
Αν έχω μια φόρμα έστω Φόρμα1 μπορώ να οδηγήσω εντολές στη Φόρμα1 με επιλογή επιπέδου. Μπορώ να τυπώνω στο βασικό επίπεδο (στη κονσόλα της Μ2000)  μέσα από άλλο με την Επίπεδο {} χωρίς όνομα δηλαδή. Στο παράδειγμα δεν χρησιμοποιούμε εξωτερικό γεγονός για να καλεί συναρτήσεις εξυπηρέτησης μηνυμάτων, και το εσωτερικό κάνει το εξής παίρνει το μήνυμα (στα αγγλικά είναι) και το ενώνει με το όνομα της φόρμας και με το όνομα του τμήματος Έτσι εμείς έχουμε φτιάξει έστω στο τμήμα Α την Φόρμα1.Click και αυτή θα υπάρχει ως Α.Φόρμα1.Click. Δείτε τι γίνεται όμως: Η κλήση της γίνεται με ένα ρυθμιστή "τοπικά"

Όρισε Φόρμα1 Φορμα
Επίπεδο Φόρμα1 {
      Παράθυρο 12, 16000,8000
      Οθόνη 2
      Πένα 15
      Φόρμα 40,20
      Τύπωσε @(0,Ύψος/2), $(6, Πλάτος), "Κεντραρισμένο Κείμενο"
}
\\ άνοιγμα παραθύρου Modal, με αναμονή για κλείσιμο, για να προχωρήσει
Συνάρτηση Φόρμα1.Click {
      \\ η συνάρτηση εξυπηρέτησης μηνύματος καλείται ως τοπική
      \\ δηλαδή με αλλαγή του ονόματος με το όνομα του τμήματος
      \\ που βρίσκεται αυτή!
      \\ για το λόγο αυτό "διαβάζεται" η Φόρμα1
      Α=Ρώτα("οκ?","Μήνυμα")
      Τύπωσε Α \\ τυπώνει στο βασικό επίπεδο
      \\ τυπωνουμε στη φόρμα
      Επίπεδο Φόρμα1 { Τύπωσε Α : Ανανέωση}
      Ανανέωση
}
Μέθοδος Φόρμα1,"Show", 1
Όρισε Φόρμα1 Τίποτα


Δείτε τη συμβαίνει με Κάλεσε Τοπικά: Η Α υπάρχει στο τμήμα και η κλήση Κάλεσε Τοπικά κάνει την συνάρτηση να έχει το ίδιο HERE$ με το τμήμα οπότε η Α βρίσκεται και αυξάνει τιμή κατά ένα.
Παρόλο που το HERE$ αλλάζει, παραμένει κάτι άλλο που δεν αλλάζει και αυτό είναι ότι κάθε νέο που θα φτιαχτεί στην συνάρτηση θα διαγραφεί. Αν ήθελα να έχω μια Α που να μην άλλαζε την Α στο τμήμα τότε θα την είχα ως Τοπική Α. Δείτε ότι η κλήση της συνάρτησης γίνεται χωρίς να ζητάμε επιστροφή τιμής. Ειδικά η επιλογή "Τοπικά" στην κάλεσε σημαίνει ότι την καλούμε ως Κενή, και αν αυτή γυρνάει τιμή τότε θα πεταχτεί (διαφορετικά θα την μετρούσε ως λάθος)

Α=10
Συνάρτηση ΜιαΤοπική {
      Α++
}


Κάλεσε Τοπικά ΜιαΤοπική()
Τύπωσε Α

θα δώσει 11 (αν από λάθος καλέσουμε την ΜιαΤοπική με το Κάλεσε Τοπική και όχι Τοπικά τότε δεν θα έχουμε πρόβλημα στη κλήση αλλά στο αποτέλεσμα, θα πάρουμε το 10. Το γράφω εδώ γιατί είναι ένα Bug...που μπορεί να συμβεί για ένα γράμμα!
(το Κάλεσε Τοπική ΜιαΤοπική() θα πετάξει το κάλεσε και θα εκτέλεσει το Τοπική ΜιαΤοπική() η οποία ισοδυναμεί με Πϊνακας ΜιαΤοπική() για τοπικό πίνακα, να γιατί δεν βγαίνει λάθος)



Ενώ το παρακάτω θα δώσει 10

Α=10
Συνάρτηση ΜιαΤοπική {
      Τοπική Α
      Α++
}


Κάλεσε Τοπικά ΜιαΤοπική()
Τύπωσε Α


το οποίο είναι ίδιο με αυτό, στο οποίο καλούμε κανονικά μια συνάρτηση (προσοχή αν η κάλεσε καλέσει συνάρτηση που αυτή γυρίζει μη μηδενικό αποτέλεσμα τότε το αποτέλεσμα θα λογαριαστεί ως αριθμός σφάλματος, Αν θέλουμε πράγματι να καλέσουμε μια συνάρτηση που δεν είναι κενή τότε το δηλώνουμε Κάλεσε Κενή ΜιαΤοπική() και θα αγνοηθεί το όποιο αποτέλεσμα).
Το HERE$ μέσα στην Συνάρτηση αλλάζει, γίνεται τοπικό. Μπορούμε να δούμε τι γίνεται με την μεταβλητή Τμήμα$ (και η συνάρτηση είναι είδος τμήματος).

Α=10
Συνάρτηση ΜιαΤοπική {
      Α=0
      Α++
      Τύπωσε Τμήμα$
}
Κάλεσε ΜιαΤοπική()
Τύπωσε Α



Μια άλλη περίπτωση κλήσης συνάρτησης με την κάλεσε είναι με χρήση της επιστροφής ως μήνυμα λάθους:

Συνάρτηση ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε =1 : έξοδος
      Τύπωσε "Χ=";Χ
}
Δες Οκ {
      Κάλεσε ΜιαΤοπική(2)
}
Αν όχι Οκ τότε τύπωσε λάθος$
Κάλεσε ΜιαΤοπική(10)


Το λάθος συμβαίνει στην γραμμή Κάλεσε ΜιαΤοπική(2) και όχι μέσα στην συνάρτηση. Απλά μέσα στην συνάρτηση χρησιμοποιούμε την επιστροφή τιμής για "σηκώσουμε" το λάθος.
Δείτε παρακάτω μια παραλλαγή με τμήμα. Η επιστροφή λάθους δίνεται περιγραφικά. Ο διερμηνευτής καλεί το τμήμα ως συνάρτηση (αν και δεν θα πάρει επιστροφή τιμής). Η διαφορά είναι στο Here$ όπου δεν θα υπάρχει το όνομα ΜιαΤοπική αλλά το A[1] (το Α από το τμήμα Α που έχω τον παρακάτω κώδικα). Το Here$ το διαβάζω με την Τμήμα$ (δουλεύει και σε συνάρτηση)

Τμήμα ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε Λάθος "Τιμή μικρότερη από 5"
      Τύπωσε "Χ=";Χ, Τμήμα$
}
Δες Οκ {
      Κάλεσε ΜιαΤοπική, 2
}
Αν όχι Οκ τότε τύπωσε λάθος$
Κάλεσε ΜιαΤοπική, 10



Αν καλέσω την ΜιαΤοπική ως τμήμα (παραμένει το όνομα ΜιαΤοπική), τότε το λάθος δεν θα μπορέσει να βγει άμεσα! Η οκ στην Δες (Try στο αγγλικό ανάλογο), παίρνει τιμή 0 αν το λάθος συμβαίνει στο επίπεδο του μπλοκ. Εδώ συμβαίνει πιο μέσα, οπότε ελέγχουμε μια πιο γενική μεταβλητή τη Λάθος η οποία μόλις διαβάζεται έχει την ιδιότητα να μηδενίζει (*)!
Επίσης το όνομα που γυρνάει η εσωτερική μεταβλητή Τμήμα$, είναι το Α.ΜΙΑΤΟΠΙΚΗ (όλα τα ονόματα τμημάτων-συναρτήσεων γίνονται κεφαλαία χωρίς τόνους).

Τμήμα ΜιαΤοπική {
      Διάβασε Χ
      Αν Χ<5 τότε Λάθος "Τιμή μικρότερη από 5"
      Τύπωσε "Χ=";Χ, Τμήμα$
}
Δες Οκ {
      ΜιαΤοπική, 2
}
\\ Αν όχι Οκ τότε τύπωσε λάθος$
Αν Λάθος τότε τύπωσε λάθος$
ΜιαΤοπική, 10



* Αν βάζαμε δυο φορές:
Αν Λάθος τότε τύπωσε λάθος$
Αν Λάθος τότε τύπωσε λάθος$

Τότε αν η Λάθος είναι αληθής θα μας δώσει μια φορά το τύπωσε λάθος$. Την επόμενη θα είναι ψευδής.

Ο λόγος που έχουμε δυο διαφορετικούς τρόπους ονομασίας ενός τμήματος (στο αντικείμενο εκτέλεσης) είναι απλός: Στις συναρτήσεις και στα τμήματα με όνομα που περιέχει αριθμό, επιτρέπεται η αναδρομή. Έτσι κάθε αναδρομική κλήση δεν θα μεγαλώνει το όνομα, αλλά μόνο το νούμερο.


Τμήμα ΚάλεσεΞανα {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { Κάλεσε ΚάλεσεΞανά, Χ-1 } αλλιώς Λίστα
}


Κάλεσε ΚάλεσεΞανά 10


Το όνομα του τμήματος είναι Α[1] (αν γράφουμε στο τμήμα Α, αλλά θα ήταν Α[2] αν το γράφαμε στο τμήμα που καλούσαμε με κάλεσε από το Α)

Συνάρτηση ΚάλεσεΞανα {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { Κάλεσε ΚάλεσεΞανά(Χ-1) } αλλιώς Λίστα
}


Κάλεσε ΚάλεσεΞανά(10)


Συνάρτηση ΚάλεσεΞανα1 {
      Διάβασε Χ
      Τύπωσε Τμήμα$
      Αν Χ>1 τότε { =ΚάλεσεΞανά1(Χ-1) } αλλιώς Λίστα : =0
}


Χ=ΚάλεσεΞανά1(10)


Συνοψίζοντας:
Είδαμε ότι η γλώσσα έχει μια συνάρτηση σαν έναν εικονικό υπολογιστή που δέχεται αντικείμενο για είσοδο-έξοδο, δέχεται το κώδικα που θα τρέξει και επενεργεί σε μια γενική μεταβλητή την HERE$. Για λόγους επιθεώρησης της εκτέλεσης η execute ενημερώνει μια διαδικασία trace, ιχνηλασίας, όπου δηλώνεται ο κώδικας που τρέχει και έτσι μπορεί να μας δίνει ο διερμηνευτής την εντολή που εκτελεί στο σημείο που εκτελεί, στο κώδικα.(στο πεδίο Print της φόρμας μπορούμε να τυπώσουμε μεταβλητές που θέλουμε. Δεν μπορούμε να αλλάξουμε τιμές όμως...εκτός και αν δώσουμε για εκτύπωση συνάρτηση που παίρνει παράμετρο...(αυτό κάνει για αντικείμενα).


Στο ερώτημα πώς βρίσκει στο σημείο ο ιχνηλάτης, ενώ δεν υπάρχει "program counter" η απάντηση είναι ότι το βρίσκει από το τέλος, ξέρει δηλαδή πόσος κώδικας απέμεινε! Υπάρχει περίπτωση να μην βγάζει συμπέρασμα, διότι μπορεί να έχουμε κάνει ένθεση κώδικα. Τότε απλά μας δίνει μια κόκκινη γραμμή για να καταλάβουμε ότι "αστόχησε". Εδώ στη φωτογραφία φαίνεται ότι ξεκίνησε το β με το δοκιμή β που άνοιξε το παράθυρο του ελέγχου. (αν είχαμε γράψει Ελληνικά πριν θα άνοιγε στα Ελληνικά)
Εδώ είναι η φόρμα στα ελληνικά και είναι το παράδειγμα με το παράθυρο που γράφουμε σε δυο επίπεδα, στο παράθυρο και στη κονσόλα. Ο ιχνηλάτης δεν ακολουθεί εντολές από τις συναρτήσεις που καλούνται από γεγονότα. Δείτε ότι είναι σταματημένος στο σημείο που έχουμε ανοίξει τη φόρμα Modal. Παρόλο που έχει σταματήσει η ροή του προγράμματος εκεί. Εξακολουθούν να τρέχουν προγράμματα (πάλι δηλαδή εκτελείται η Execute) γιατί την ξεκινάνε τα μηνύματα από τη φόρμα (ή όπως θα διαβάσει κανείς και το εγχειρίδιο τεύχος 12, μπορεί να φτιάξει νήματα, δηλαδή τμήματα κώδικα που εκτελούνται κάθε ένα χρονικό διάστημα, Υπάρχουν παραδείγματα όπου νήματα και μηνύματα εκτελούνται χωρίς κανένα πρόβλημα.
Πώς ξέρει ο ιχνηλάτης ότι δεν θα ακολουθήσει ένα μήνυμα; Το ξέρει η Execute και το διαβάζει από μια ιδιότητα του αντικειμένου εκτέλεσης basetask και λέγεται IamAnEvent,
Η εντολή οθόνη 2 έδωσε το πράσινο χρώμα στην φόρμα. Η Φόρμα 40, 20 έκανε το μέγεθος του παραθύρου να παίρνει 40 χαρακτήρες επί 20 γραμμές. (ενώ δώσαμε ένα μέγεθος, η Φόρμα το έκοψε στα μέτρα της). Η εντολή φόρμα δουλεύει και για την κονσόλα (γενικά όλες οι εντολές δουλεύουν και στα παράθυρα, εκτός από τις εντολές εισόδου.



Περισσότερα σε επόμενη ανάρτηση...

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.