Παρασκευή 29 Οκτωβρίου 2021

Πώς δουλεύει ο διερμηνευτής της Μ2000 (Έκδοση 10) - Ανανέωση!

Σκοπός του διερμηνευτή της Μ2000 δεν είναι η ταχύτητα εκτέλεσης, αλλά η σωστή εκτέλεση όλων αυτών που μπορεί να κάνει η Μ2000.

Από την αρχή επιλέχθηκε ο κώδικας να είναι όσο πιο απλός γίνεται. Ασφαλώς αυτό δεν ισχύει σήμερα. Με τις προσθήκες έχει γίνει τεράστιος! Υπήρξαν στάδια που "ξεχείλωναν" το πρόγραμμα!

Η πρώτη ιδέα του σωρού τιμών ήταν να βρίσκεται σε ένα αλφαριθμητικό αριθμοί και αλφαριθμητικά μαζί. Σε δεύτερο χρόνο ο σωρός τιμών έγινε ένα αντικείμενο που κουβαλάει άλλα αντικείμενα σε μια συλλογή. Επίσης σε τρίτο χρόνο φτιάχτηκε μια δεξαμενή αντικειμένων απ΄ όπου τραβάει ο οποιοσδήποτε σωρός ελεύθερα αντικείμενα, και ρίχνει πίσω ότι ελευθερώνεται, Αυτός ο τρόπος αύξησε την ταχύτητα γιατί δεν χρειάζονταν να δημιουργούνται νέα αντικείμενα, ούτε να διαγράφονται. Και οι δυο ενέργειες, δημιουργίας και διαγραφής κοστίζουν σε χρόνο. Αν δεν έχει άλλα η δεξαμενή να δώσει δημιουργούνται νέα. Με μια αρχική τιμή μερικών χιλιάδων σπάνια να χρειαστούν άλλα!

Το αντικείμενο σωρός είναι στο αρχείο mStiva.cls (το cls σημαίνει κλάση).

Όταν καλούμε μια δική μας συνάρτηση τότε όλα εκείνα που δίνουμε ως τιμές παραμέτρων (τα ορίσματα δηλαδή)  πάνε στο σωρό τιμών της συνάρτησης που θα κληθεί, ο οποίος είναι νέος σωρός όταν η συνάρτηση καλείται σε έκφραση. Εξαιρούνται οι στατικές συναρτήσεις (θα δούμε για αυτές αργότερα).

Ο σωρός αρχικά μπορούσε να πάρει αριθμούς και γράμματα. Όμως μπορεί να πάρει και αντικείμενα. Πχ μπορούμε να βάλουμε ένα πίνακα (είναι αντικείμενο στη Μ2000). Γενικά το γράψιμο στο σωρό είναι με τιμή "πάντα". Από την 5η έκδοση της γλώσσας είχε μπει ο τρόπος να περάσουμε αναφορές.

Οι μεταβλητές απαιτούν δυο δομές: Έναν πίνακα τιμών, και ένα "λεξικό" που στη Μ2000 λέγεται κατάσταση τιμών. Ο πίνακας είναι τύπου Variant (παίρνει οτιδήποτε) και μπορεί να αυξομειώνεται. Το λεξικό δουλεύει με δυο πίνακες, ο ένας έχει για κάθε στοιχείο του μια ομάδα τιμών (UDT ή user defined type, κατά μια έννοια κάτι όπως το struct της C), και ο άλλος είναι πιο απλός, λέγεται πίνακας Κατακερματισμού (Hash Table), λέει πχ στη θέση Κ ότι έχει το στοιχείο Μ του πρώτου πίνακα. 

Ενδιαφέρον παρουσιάζει εδώ να κατανοήσει κανείς πως μεγαλώνουν αυτοί οι πίνακες και πώς χρησιμοποιούνται από τη Μ2000 για να βρίσκει τις μεταβλητές. Οι μεταβλητές διαγράφονται με σειρά ανάποδη από τη σειρά που δημιουργούνται. Ο πίνακας κατακερματισμού "αφαιρεί" πχ τρεις μεταβλητές άμεσα, γιατί ξέρει που είναι στο πίνακα Hash, οπότε "καθαρίζει" και απλά ένας δείκτης μειώνεται κατά 3. Η αναζήτηση στο πίνακα Hash γίνεται άμεσα! Το χρονοβόρο είναι το μέγεθος του ονόματος. Μια συνάρτηση Hash (χρησιμοποιείται εδώ η κλασική του συστήματος των Windows, που είναι πάντα φορτωμένη στη πιο γρήγορη μνήμη του επεξεργαστή), λέγεται HashData. Παλαιότερα είχα δική μου συνάρτηση γιατί τα ονόματα των μεταβλητών "ξεχείλωναν" με το πρόθεμα τους (που σχετίζεται με το που δημιουργήθηκαν), οπότε αντί να διάβαζα όλα τα γράμματα έριχνα το βάρος στο όνομα της μεταβλητής και έπαιρνα λιγότερα στοιχεία από το πρόθεμα. Στις τελευταίες εκδόσεις τα προθέματα έχουν γίνει σαν "συντομεύσεις" με ένα γράμμα και έναν αριθμό, οπότε το κλειδί για τον HashData δεν είναι μεγάλο. Για να δουλέψει ο πίνακας κατακερματισμού πρέπει να έχουν φτιαχτεί δυο κλειδιά. Το πρώτο βγαίνει από το όνομα ως ένας 32bit αριθμός. Το δεύτερο βγαίνει βάσει του μεγέθους του πίνακα κατακερματισμού, και δείχνει τη θέση στο πίνακα. Οπότε στο βοηθητικό πίνακα με τα UDT υπάρχει πεδίο που φυλάσσεται το πρώτο κλειδί για το λόγο ότι αν αλλάξει ο πίνακας HASH μέγεθος  τότε πρέπει να υπολογιστούν τα δεύτερα κλειδιά από τα πρώτα. Αν έχουμε πενήντα μεταβλητές τότε αυτό σημαίνει ότι θα πάρουμε 50 αριθμούς των 32bit, θα βγάλουμε το υπόλοιπο (mod) βάσει του μεγέθους του πίνακα και βρήκαμε τις θέσεις. Ο πίνακας κατακερματισμού είναι μεγαλύτερος από τον πίνακα με τα UDT, αλλά ενδέχεται να έχουμε "σύγκρουση" δηλαδή δύο στοιχεία να έχουν το ίδιο τελικό κλειδί. Αυτό λύνεται ως εξής. Όταν στο δεύτερο διαπιστωθεί ότι υπάρχει ήδη κάποιο στο πίνακα HASH τότε παίρνουμε από εκεί τον δείκτη που δείχνει στο πίνακα των UDT και τον βάζουμε σε πεδίο στο νέο στοιχείο και θα βάλουμε το δικό του "νούμερο" ή θέση στον πίνακα των UDT, στη θέση που δείχνει το κλειδί για τον HASH πίνακα. Άρα αν φτιαχτούν διαδοχικά δυο ας πούμε Γενικές μεταβλητές με όνομα Α, τότε και οι δύο βγάζουν τον ίδιο κωδικό HASH, αλλά στο πίνακα HASH πρώτα εμφανίζεται η τελευταία που μπήκε. Δηλαδή για ίδια ονόματα έχουμε το LIFO, το τελευταίο που μπήκε θα βγεί πρώτο (στην αναζήτηση αλλά και στη διαγραφή). Αυτός είναι και ο μηχανισμός σκίασης!

Στο σημείο αυτό ας δούμε και κάτι ακόμα για τις μεταβλητές. Είδαμε ότι οι δυο πίνακες του Λεξικού (ή Κατάσταση) δεν είναι ίδιες σε αριθμό στοιχείων. Ακριβώς το ίδιο συμβαίνει και με το πίνακα μεταβλητών (που κρατάμε τις τιμές τους) και τον πίνακα UDT. Όμως ο λόγος που συμβαίνει είναι διαφορετικός.  Μπορούμε να έχουμε πχ τρεις τιμές στο 0, 1 και 2 του πίνακα τιμών και τέσσερα ονόματα Α, Β, Γ, Δ όπου δυο από αυτά να δείχνουν σε μια τιμή, έστω στην 2. Υπάρχει ένα πεδίο στο UDT που λέει ότι το όνομα είναι "αναφορά". Κανονικά όταν διαγράφουμε όνομα από το λεξικό μηδενίζουμε και τη τιμή του. Αν όμως έχουμε "σήμα" ότι το όνομα είναι αναφορά θα αφήσουμε την τιμή ως έχει. Εξ ορισμού μια μεταβλητή που είναι αναφορά δεν παίρνει άλλη αναφορά, έτσι πάντα η αρχική μεταβλητή βρίσκεται σε "παλαιότερη" θέση. Αν αλλάζαμε τιμές θα γίνονταν "χαμός", δεν θα μπορούσε να λειτουργήσει σωστά το σύστημα! Θα δούμε αργότερα ότι υπάρχουν μεταβλητές που λέγονται δείκτες και αλλάζουν αυτό που δείχνουν. Όμως αυτά που δείχνουν δεν βρίσκονται στο χώρο των μεταβλητών!

Με απλά λόγια ο χώρος των μεταβλητών είναι αυτός που σχετίζεται με τα ονόματα. Οι τιμές είτε μένουν σε αυτόν το χώρο είτε αντί τιμής να υπάρχει δείκτης σε αντικείμενο, όπου το αντικείμενο έχει δικό του χώρο. Κατά μια έννοια, αλλά χωρίς να είναι αντικείμενο, συμβαίνει και στα αλφαριθμητικά να έχουμε ένα δείκτη στο χώρο μεταβλητών και ο χώρος που γράφονται τα γράμματα να είναι αλλού. Εδώ αξίζει να σημειωθεί ότι τα αλφαριθμητικά είναι μιας εγγραφής. Δηλαδή, όταν θέλουμε να προσθέσουμε δυο αλφαριθμητικά Α$ και Β$ (δείτε το $ είναι απαραίτητο για τον διερμηνευτή της Μ2000), δηλαδή να τα συνδέσουμε σε ένα, και να τα βάλουμε στο Α$ τότε η Μ2000 φτιάχνει ένα νέο αλφαριθμητικό, από την έκφραση A$+B$ και μετά πάει σε αυτό που έχει το Α$ και κάνει αλλαγή των δεικτών, ώστε εκείνο που φτιάχτηκε να μην διαγραφεί αλλά να διαγραφεί στη θέση του το παλιό του A$. Παλαιότερα έβγαινε ένα αντίγραφο του A$+B$ ως νέο αλφαριθμητικό για το A$ και μετά διαγράφονταν αυτό της έκφρασης, δηλαδή κάποια στιγμή υπήρχαν δυο αλφαριθμητικά A$+B$. Με την βελτιστοποίηση να γίνεται αλλαγή δεικτών (swap), κέρδισε σε ταχύτητα ο διερμηνευτής!

Στις πρώτες εκδόσεις της γλώσσας οι προγραμματιστικές δομές με όνομα ήταν δυο: Τα τμήματα και οι συναρτήσεις. Η διαφορά τους ήταν ως προς το τρόπο κλήσης εσωτερικά. Μερικές διαφορές

  1. Τα τμήματα δεν έχουν αναδρομή, ενώ οι συναρτήσεις έχουν.
  2. Τα τμήματα δεν ξεκινούν με δικό τους σωρό τιμών αλλά δέχονται τον "γονικό". Αυτό σημαίνει ότι στη κορυφή του σωρού (ειδική στοίβα, μπορεί κανείς να τη λέει και στοίβα), θα υπάρχουν οι τιμές που περάσαμε και μετά θα ακολουθεί ότι ήδη υπάρχει. Άρα μπορούμε να αφήσουμε κάτι. Τα τμήματα μπορούν να γυρίσουν τιμές στο σωρό τιμών. 
  3. Οι συναρτήσεις μπορούν να περαστούν με αναφορά
  4. Τα τμήματα έχουν μια ειδική λειτουργία που μπορούν κατά τη κλήση να αλλάξουν εσωτερικά τμήματα πρόσκαιρα (για τη κλήση αυτή)
Μερικές ομοιότητες:
  1. Και τα τμήματα και οι κανονικές συναρτήσεις (λέγονται κανονικές γιατί υπάρχουν και άλλες), έχουν δικό τους όνομα χώρου. Δηλαδή όταν δημιουργούμε μια μεταβλητή, πχ την Α τότε θα δώσει ο διερμηνευτής ένα πρόθεμα σχετικό με το χώρο που δημιουργήθηκε, και τη σειρά δημιουργίας του χώρου.
  2. Ότι όνομα (αναγνωριστικό) δημιουργούμε θα διαγραφεί στο τέλος εκτέλεσης.
  3. Κάθε "μονάδα" είτε είναι τμήμα είτε κανονική συνάρτηση, έχει δικό του ένα αντικείμενο τύπου Basetask. Επειδή ο διερμηνευτής είναι ένα σύνολο συναρτήσεων όπου καλούνται η μία μεσα στην άλλη, μεταφέρεται μαζί με βασικές παραμέτρους και το αντικείμενο τύπου Basetask. Σε αυτό υπάρχει ο δείκτης προς τον "τωρινό" σωρό τιμών, καθώς επίσης και ο δείκτης για το αντικείμενο εξόδου (πχ η κοσνόλα, ή ένα επίπεδο πάνω στη κονσόλα, ή μια φόρμα του γραφικού περιβάλλοντος που μπορούμε να δημιουργήσουμε, ή ο εκτυπωτής που έχουμε επιλέξει). Οι εντολές εξόδου είναι ίδιες για οποιοδήποτε αντικείμενο εξόδου (εκτός από κάποιες εντολές που δεν υποστηρίζονται, όπως πχ μια εντολή να καθαρίσουμε το αντικείμενο εξόδου όταν αυτό είναι ο εκτυπωτής - απλά δεν γίνεται, απαιτείται εντολή αλλαγής σελίδας και επιλογή προσανατολισμού, οριζόντιου ή κάθετου).
  4. Και τα τμήματα και οι συναρτήσεις μπορούν να έχουν στατικές μεταβλητές. Δηλαδή μεταβλητές που μετά την αρχικοποίησή τους, θα έχουν τιμές που θα τις ξαναβρούμε σε επόμενες κλήσεις.
Ειδικά για το (4) παραπάνω, αλλά και σε σχέση με το (3): Στην κονσόλα όταν ξεκινάμε τη Μ2000 υπάρχει ο αρχικός Basetask τύπος αντικειμένου. Η εκτέλεση από το σημείο αυτό γίνεται με κλήση στο εσωτερικό Interpreter. Αυτό το μέρος δεν εκτελεί επαναλήψεις, αλλά μπορεί να ξεκινήσει τμήματα ή συναρτήσεις που έχουμε γράψει (του χρήστη δηλαδή) και αυτές θα εκτελεστούν στο εσωτερικό Execute. Το execute αναγνωρίζει τα Statements, τις εντολές κατά μια έννοια (ομοίως και ο Interpreter αλλά σε υποσύνολο, και με μερικές δικές του εντολές). Σε αυτά μπορούν να συμβαίνουν τα παρακάτω:

Πίνακας Αναγνώρισης Εντολών του Execute

  1. Σημείωση (πχ REM η ΣΗΜ η ' ή // ή \\)
  2. Όνομα χωρίς να ακολουθεί τελεστής και χωρίς παρενθέσεις
  3. Όνομα χωρίς να ακολουθεί τελεστής με παρενθέσεις
  4. Γνωστό όνομα δομής (είτε για ορισμούς είτε για αλλαγή ροής εκτέλεσης)
  5. Όνομα (με $ ή με % στο τέλος ή χωρίς κάτι από αυτά) με τελεστή
  6. Όνομα (με $ ή με % στο τέλος ή χωρίς κάτι από αυτά) με παρενθέσεις και με τελεστή
  7. Κενή γραμμή
  8. Όνομα με το : στο τέλος χωρίς να ακολουθεί άλλο εκτός από  κενό, αλλαγή γραμμής ή σημείωση.
  9. Αριθμός μέχρι πέντε ψηφία το 01 και το 00001 είναι το ίδιο το 1
  10. Αγκύλη {  ή  }
  11. Σύμβολο : (σημαίνει αλλαγή εντολής οριζόντια)
  12. Αλλαγή γραμμής (σημαίνει αλλαγή εντολής κάθετα).

Το (2) μπορεί να είναι εντολή πχ Τύπωσε 10 ή ένα όνομα τμήματος. Για το λόγο αυτό αν δώσουμε κάτι άγνωστο, πχ ΑΦΧΨ μας επιστρέφει μήνυμα ο διερμηνευτής ότι δεν υπάρχει τέτοιο τμήμα. 
Το (3) είναι κλήση ρουτίνας. Αν δεν την βρει τότε έχουμε λάθος. Οι ρουτίνες είναι στατικά επώνυμα μέρη κώδικα. Βρίσκονται στο τέλος του τμήματος ή της συνάρτησης. Με την πρωτη κλήση ο διερμηνευτής καταγράφει τη θέση της στο αντικείμενο τύπου Basetask. 
Διαφορές Ρουτινών και Τμημάτων:
  1. Οι ρουτίνες δεν έχουν όνομα χώρου. Οπότε αν θέλουμε να ορίσουμε νέα μεταβλητή πρέπει να τη δηλώσουμε ως τοπική
  2. Οι ρουτίνες έχουν υποχρεωτικά παρενθέσεις ακόμα και αν δεν δηλωθούν παράμετροι. Εκτελούνται με τη Διαμέσου ή χωρίς. Αν υπάρχει πίνακας με ίδιο όνομα τότε ο διερμηνευτής θα "πιστέψει" ότι είναι πίνακας και θα βγάλει λάθος επειδή λείπει τελεστής! 
  3. Η έξοδος από τη ρουτίνα γίνεται με το Τέλος Ρουτίνας ή το Έξοδος Ρουτίνας. Η έξοδος από ένα τμήμα γίνεται στο τέλος του κώδικά του, ή με την έξοδος. Ομοίως για τη Συνάρτηση, η έξοδος γίνεται όπως και στο τμήμα, και όχι με την επιστροφή τιμής.
  4. Οι ρουτίνες επειδή βλέπουν ό,τι έχει το τμήμα ή η συνάρτηση που βρίσκονται μπορούν να τα χρησιμοποιήσουν. Έτσι μπορούν να καλέσουν τον εαυτό τους. Οι ρουτίνες έχουν αναδρομή.
  5. Οι ρουτίνες δεν έχουν δικό τους σωρό τιμών αλλά μπορούμε να δηλώσουμε Σωρός Νέος { } και μέσα στο μπλοκ να έχουμε ένα καθαρό σωρό.
  6. Στο σωρό τιμών μπορούν να περαστούν μεταβλητές με τιμή ή με αναφορά. Γενικά με αναφορά δεν βολεύει αφού υπάρχει θέαση σε όλο το τμήμα, εκτός και αν έρχεται από κάπου αλλού!
  7. Ενώ ένα τμήμα είναι δυναμικό, δηλαδή μπορεί να αλλάξει κώδικα με νέο ορισμό, οι ρουτίνες δεν είναι δυναμικές, αλλά στατικές στο τμήμα ή τη συνάρτηση που βρίσκονται. 
  8. Στα τμήματα και τις κανονικές συναρτήσεις οι ορισμοί βρίσκονται πριν την χρήση τους, ενώ στις ρουτίνες και τις απλές συναρτήσεις οι ορισμοί βρίσκονται στο τέλος του τμήματος ή της συνάρτησης που φτιάχνονται.
  9. Μια ρουτίνα μπορεί να καλέσει μια "αδελφή" ρουτίνα (έχουν και οι δύο τον ίδιο γονέα, το τμήμα ή τη συνάρτηση που ανήκουν), ενώ ένα τμήμα δεν αναγνωρίζει "αδέλφια" αλλά μόνο αυτό (υο παιδί) στο οποίο είναι απευθείας γονικό, και σε ότι είναι γενικό. Ένα τμήμα Α αν ορίζει ένα τμήμα Β και το Β ένα τμήμα Γ τότε το Α δεν βλέπει ο Γ. Αν καλέσουμε από το Α το Β και τερματίσει το Β τότε δεν θα υπάρχει Γ στο σύστημα, επειδή ο ορισμός του έγινε στο Β στην επιστροφή από το Β θα διαγραφεί. Υπάρχει μηχανισμός που κρατάει σε πακέτα τις στατικές μεταβλητές και τις φορτώνει στην επόμενη κλήση της Β. Έτσι οι στατικές μεταβλητές σχετίζονται με το γονέα βάσει κλήσης. Ένα τοπικό τμήμα καλείται από ένα μόνο τμήμα. Αν γενικό τμήμα μπορεί να κληθεί από άλλα όσο δεν τερματίζει το τμήμα που το δημιούργησε. Σε κάθε περίπτωση κλήσης του γενικού τμήματος, οι τυχόν στατικές μεταβλητές θα διαφέρουν βάσει του ποιου τμήματος έγινε η κλήση. Έτσι αν το ΑΑ είναι γενικό και έχει στατικές μεταβλητές και για τα τοπικά Β, Δ ενός τμήματος Ζ, το ΑΑ θα έχει δυο σετ στατικών, ένα για τη  σειρά κλήσεων Ζ μετά Β μετά ΑΑ και ένα για τη σειρά Ζ μετά Δ μετά ΑΑ. Στις κανονικές συναρτήσεις οι στατικές στην αναδρομή είναι ίδιες για όλο το βάθος αναδρομής. Οι στατικές στην έξοδο γίνονται πακέτο και μπαίνουν στη συλλογή στατικών του προηγούμενου Basetask. Στην αναδρομή σε συναρτήσεις βγαίνει νέο αντικείμενο τύπου Basetask και σε αυτό απλά γράφεται ο δείκτης στην ίδια συλλογή στατικών με αυτό που έχει το πρώτο Basetask, στη πρώτη κλήση της συνάρτησης. 
  10. Μια ρουτίνα δεν μπορεί να ορίσει ρουτίνες ή απλές συναρτήσεις. Ένα τμήμα και μια κανονική συνάρτηση μπορούν να ορίζουν άλλα τμήμα και συναρτήσεις καθώς και ρουτίνες και απλές συναρτήσεις. Σε μια ρουτίνα μπορούμε να ορίσουμε τμήματα και συναρτήσεις, και θα είναι ορατά μέχρι να τερματίσει η ρουτίνα. Αν όμως έχουμε ίδια ονόματα με τμήματα και συναρτήσεις που είναι ήδη ορισμένα και δεν είναι "τελικά", τότε θα αλλάξουμε το κώδικά τους!
  11. Μια ρουτίνα (όπως και οι απλές συναρτήσεις) δεν έχουν δικό τους αντικείμενο τύπου Basetask, άρα δεν έχουν και δικές τους στατικές. Αν υπάρχουν τις δημιουργούν. Οι στατικές δεν μπορούν να περαστούν με αναφορά σε ρουτίνες ή απλές συναρτήσεις. Μπορούν να περαστούν με αναφορά σε κλήσεις συναρτήσεων και τμημάτων
Γενική Απαρίθμηση Καθαρισμός {Νέες_Στατικές, Ως_Έχουν_Στατικές}
Τμήμα Γενικό χρήσηΣτατικής (ΝαΚαθαρίσω){
      Αν ΝαΚαθαρίσω = Νέες_Στατικές τότε Καθαρό
      Τύπωσε Γράμμα$
      Στατική Κ=100
      Συνάρτηση Κάππα(&Μ) {
            =Μ^2
            Μ++
      }
      Τύπωσε Κάππα(&Κ)
      Τύπωσε Κάππα(&Κ)
      Τύπωσε Κάππα(&Κ)
}
Τμήμα Αλφα {
      Στατική Επιγραφή$="Από το τμήμα Άλφα"
      χρήσηΣτατικής Νέες_Στατικές, Επιγραφή$
      χρήσηΣτατικής Ως_Έχουν_Στατικές, Επιγραφή$ + " συνέχεια"
}
Τμήμα Βήτα {
      Στατική Επιγραφή$="Από το τμήμα Βήτα"
      χρήσηΣτατικής Νέες_Στατικές, Επιγραφή$
      χρήσηΣτατικής Νέες_Στατικές, Επιγραφή$
}
Άλφα
Βήτα

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

Οι ομοιότητες είναι:
  1. Όπως τα τμήματα και οι συναρτήσεις, οι ρουτίνες σβήνουν ότι έχουν δημιουργήσει με το πέρας εκτέλεσης, είτε το κανονικό στο Τέλος Ρουτίνας, είτε με έξοδο με το Έξοδος Ρουτίνας, είτε με κάποιο λάθος που μπορεί να συμβεί.
  2. Μπορούμε να καλέσουμε ότι υπάρχει μέσα στο τμήμα ή τη συνάρτηση, πχ άλλα τμήματα ή συναρτήσεις ή ρουτίνες ή απλές συναρτήσεις καθώς και ότι είναι γενικό.
Οι ρουτίνες είναι λίγο πιο γρήγορες (μετά την πρώτη κλήση) επειδή δεν έχουν δημιουργία νέου αντικειμένου Basetask. Το ίδιο συμβαίνει και με τις απλές συναρτήσεις. Οι απλές συναρτήσεις γράφονται έτσι:

Τύπωσε @όνομα(3)#Αθρ()=36, @όνομα(3)#Τιμη(1)=27

(κ, λ)=@όνομα(3)
Τύπωσε κ=9, λ=27

Συνάρτηση όνομα(χ
      =χ**2, χ^3
Τέλος Συνάρτησης


Έξοδο με την Έξοδο Συνάρτησης και κλήση με το @όνομα(). Το σύμβολο @ λέει ότι έχουμε απλή συνάρτηση. Επίσης και εδώ θέλει προσοχή, δεν έχουμε νέο σωρό τιμών. Η συνάρτηση δηλαδή έχει πρόσβαση στο σωρό τιμών του τμήματος ή της συναρτησης που ανήκει. Μπορούμε να γυρίσουμε περισσότερες από δυο τιμές. 

Ο Διερμηνευτής ελέγχει το τύπο επιστροφής μόνο ως προς τον βασικό: Αριθμός ή Γράμματα. Μια αριθμητική συνάρτηση γυρνάει αριθμό ή αντικείμενο (όπως εδώ). Μια αλφαριθμητική μπορεί να γυρίσει γράμματα ή αντικείμενο που γυρνάει αλφαριθμητικά. Αν θέλαμε να δώσουμε στην όνομα() μια πρώτη τιμή γράμματα (αλφαριθμητικό) τότε θα τα βάζαμε όλα μέσα σε ένα ζευγάρι παρενθέσεων μετά το ίσον: ("γράμματα", χ**2, χ^3). Το ** και τι ^ είναι η ύψωση σε δύναμη.


Το αντικείμενο τύπου Basestack έχει λίστα με τις θέσεις των απλών συναρτήσεων και των ρουτινών, καθώς και των απλών ρουτινών. 

Οι απλές ρουτίνες λειτουργούν με την Διαμέσου και δεν έχουν παραμέτρους. Δίνουμε ετικέτα ή αριθμό γραμμής (που έχουμε ήδη βάλει). Στο τέλος της απλής ρουτίνας υπάρχει η εντολή Επιστροφή. Μπορούμε να έχουμε και αλλού την εντολή Επιστροφή, δηλαδή δεν χρειάζεται έξοδος με την εντολή Έξοδος. Ο σκοπός των απλών ρουτινών είναι να χρησιμοποιούμε κώδικα πολλές φορές σαν να ήταν στη θέση της κλήσης. Αν φτιάξουμε κάτι σε απλή ρουτίνα θα μείνει και μετά την επιστροφή της. Δηλαδή δεν έχουμε διαγραφή νέων ορισμών όπως στην ρουτίνα με παρενθέσεις. Δοκιμάστε να βγάλετε το Λ=0 (πχ μπορείται να το σκιάσεται με ένα ' ή ένα / ή ένα \ στην αρχή, ή με το Σημ ή REM στην αρχή). Θα βγει λάθος γιατί η ΔεςΑυτόΛ θα φτιάξει νέα Λ και θα την σβήσει, οπότε στο Αν Λ<10 το Λ δεν θα υπάρχει! Για να τρέξετε το πρόγραμμα ανοίξτε τη Μ2000, γράψτε σ α και πατήστε το πλήκτρο Enter, ανοίγει ο διορθωτής και εκεί κάντε επικόλληση το κώδικα. Πατήστε το πλήκτρο Esc και γράψτε α και πατήστε το πλήκτρο Enter. Τρέξτε το κώδικα και με Δοκιμή α. Επίσης βάλτε πριν την εντολή Τέλος την εντολή Λίστα (δείχνει τι μεταβλητές έχει εκείνη τη στιγμή ο διερμηνευτής και τις τιμές τους). Για να παρεμβάλουμε νέα γραμμή στον διορθωτή πατάμε Enter στην αρχή ή στο τέλος μιας γραμμής ανάλογα που θέλουμε να γίνει η παρεμβολή γραμμής. Με Ctrl+Enter ανοίγουμε μπλοκ { } και μπαίνει αυτόματα εσοχή στην πρώτη γραμμή ανάμεσα στο μπλοκ. Δοκιμάστε και τα Ctrl+Z (undo) και Ctrl+U (redo). Επίσης δείτε και το σχετιζόμενο μενού, βγαίνει με δεξί κλικ στο ποντίκι καθώς και με το shift F10, όπως και με αριστερό κλικ στην επικεφαλίδα του διορθωτή. Το σχετιζόμενο μενού έχει εντολές που δεν φαίνονται αρχικά αλλά βγαίνουν με ολίσθηση. Επίσης μπορούμε όταν είναι ανοιχτό το μενού να πατήσουμε αριθμούς γραμμών και να μας πάει στη γραμμή που θέλουμε! Το μενού αυτό μπορεί να μετακινηθεί και παραμένει ανοικτό όσο δίνουμε αριθμούς γραμμών και enter. Επίσης δοκιμάστε να κάνετε κλικ πάνω στην ετικέτα και με F3 θα σας πάει στο επόμενο ίδιο, ενώ με F2 στο προηγούμενο ίδιο. Με F5 αλλάζεται το όνομα παντού (υπάρχει UNDO), ενώ με F4 κάνετε το όνομα να έχει ίδια πεζά και κεφαλαία παντού στο κείμενο (σε αυτό δεν υπάρχει UNDO). Το σχετιζόμενο μενού είναι είτε με αγγλικά είτε με ελληνικά. Αν γράψουμε Ελληνικά ή Λατινικά (αλλάζει τη γλώσσα μηνυμάτων λαθών και επιγραφών στο μενού). Η εντολή Λατινικά δεν έγινε Αγγλικά λόγω συμβατότητας με την αρική Μ2000, η οποία δεν δούλευε σε Unicode, και έβαζε το λατινικό σύνολο χαρακτήρων (οπότε τα ελληνικά τα χάναμε...στον τότε διορθωτή, που δεν είχε χρωματισμό κώδικα και ήταν ένα απλό textbox της VB6).

Λ=0
για ι=1 έως 30
      διαμέσου ετικέτα
επόμενο
Τέλος


ετικέτα:
      κ=20
200αν κ<10 τότε 400
      τύπωσε κ
      κ--
      διαμέσου ΔεςΑυτότοΛ()
      αν Λ>100 τότε προς ετικέτα
      προς 200
400τύπωσε "ΟΚ - Επιστροφή από:"; ι
      Επιστροφή


Ρουτίνα ΔεςΑυτότοΛ()
      Λ=Τυχαίος(95, 103)
Τέλος Ρουτίνας


Η ετικέτα είναι όπως στη περιγραφή (8) στο πίνακα παραπάνω με την άνω και κάτω τελεία.
Αν έχουμε ένα τμήμα Αλφα τότε το Αλφα: Τύπωσε 10 θα καλέσει το Αλφα και μετα στην επιστροφή θα εκτελέσει την Τύπωσε. Ξέρει ο διερμηνευτής ότι αφού ακολουθεί κάτι μετά την άνω και κάτω τελεία και δεν είναι η αλλαγή γραμμής ή μια σημείωση ότι το Άλφα δεν είναι ετικέτα.

Ο Execute πρώτα πρέπει να ανανγνωρίσει τι έχει όπως ο πίνακας παραπάνω γράφει. Αν έχουμε εντολή τότε κοιτάει αν είναι γνωστή εντολή. Αυτό γίνεται πολύ γρήγορα με πίνακα κατακερματισμού. Αυτή η αναζήτηση μπορεί να γυρίσει ένα από πέντε πράγματα:
  1. Το όνομα έχει παρένθεση, θα είναι πίνακας ή ρουτίνα, παράκαμψη...Α
  2. Υπάρχει εντολή του Execute, στη λίστα εντολών, δίνει έναν αριθμό και αυτός πάει σε μια On X Goto....και άμεσα γίνεται άλμα στο σημείο του Execute που είναι ο κώδικας της εντολής. Οι εκτέλεση εντολών εδώ γίνεται χωρίς άλμα έξω από το Execute.
  3. Υπάρχει εντολή και μαζί μας δίνεται και μια διεύθυνση που θα καλέσουμε το πρόγραμμα για να εκτελεστεί η εντολή και να επιστρέψουμε στο Execute.
  4. Κοιτάμε αν έχουμε τελεστή εκχώρησης ή αλλαγής τιμή, αν ναι τότε έχουμε μεταβλητή
  5. Κοιτάμε αν έχουμε τμήμα, αν ναι καλούμε το τμήμα
Το ερώτημα εδώ που προκύπτει είναι: Που καλούμε συναρτήσεις; Η απάντηση:
  1. Συναρτήσεις γράφονται σε δεξιές ή αριστερές εκφράσεις
  2. Ότι έχουμε δεξιά από ένα σύμβολο εκχώρησης ή αλλαγής τιμής είναι δεξιά έκφραση.
  3. Αριστερή έκφραση είναι ο δείκτης ενός πίνακα: πχ Α(δεξιάΈκφραση1)=δεξιάΈκφραση2, αριστερή έκφραση είναι το Α(δεξιάΈκφραση1), εδώ θα εκτελεστεί πρώτα η αριστερή και μετά η δεξιά έκφραση. Όμως στην εντολή Στη (Let) θα γίνει το ανάποδο θα εκτελεστεί η δεξιά έκφραση, και μετά η αριστερήΈκφραση.
  4. Δεξιά έκφραση είναι και οι εκφράσεις εντός ρουτίνας: πχ Α(δεξιάΈκφραση)
  5. Δεξιά έκφραση είναι και οι εκφράσεις σε ένα τμήμα: πχ Α δεξιάΈκφραση, ΆλληΈκφραση... και σε μια συνάρτηση ή απλή συνάρτηση.
Υπάρχουν λοιπόν συναρτήσεις, δυο, για εκφράσεις δεξιές, που μπορεί να είναι για επιστροφή αλφαριθμητικού ή αριθμού (αντικειμένου). Η IsStrExp() και η IsExp(). Η δεύτερη βρίσκει επιπλέον αν έχουμε λογική έκφραση, ή αν έχουμε αυτόματο πίνακα: (1,2,3). Σε κάθε περίπτωση υπάρχει μια μέθοδος AheadStatusFast που βρίσκει μπροστά αν έχει έκφραση αλφαριθμητική ή αριθμητική. Αυτή η μέθοδος είναι πολλή γρήγορη γιατί κοιτάει τα $ στα ονόματα, και τους τελεστές ενώ αποφεύγει ότι είναι μέσα σε παρενθέσεις. Έτσι το $ βοηθάει να βρίσκει τι έχει μπροστά του χωρίς να κάνει επικύρωση αν τα ονόματα υπάρχουν.

Η IsExp έχει ορισμένες δικές της συναρτήσεις (η VB6 είναι flat, δηλαδή όλα σε ένα αρχείο BAS είναι θεατά, και δεν υπάρχουν συναρτήσεις μέσα σε συναρτήσεις, είναι όλα Γενικά, εκτός από τις τοπικές μεταβλητές). Έτσι όταν λέμε εδώ "δικές της" συναρτήσεις λέμε για συναρτήσεις που γράφτηκαν για να εξυπηρετηθεί η IsExp. Η πιο εσωτερική είναι η IsNumberNew. Σε αυτήν γίνεται χρήση λίστας ονομάτων αριθμητικών συναρτήσεων για να βρεθεί αν το όνομα είναι συνάρτηση της Μ2000, συνάρτηση του χρήστη, συνάρτηση απλή του χρήστη, σε άλλη λίστα  αριθμητική μεταβλητή μόνο για ανάγνωση, πίνακες, μεταβλητές. Πριν κληθεί η IsNumberNew, έχει κληθεί η IsNumber η οποία κοιτάει αν έχουμε αριθμό με νούμερα πχ 12345, επίσης κοιτάει αν έχουμε σύμβολο τύπου πχ το & είναι για 32bit ακέραιο, ενώ το @ είναι για 27 ψηφία αριθμό (Decimal), και υπάρχουν και άλλα. Επίσης κοιτάει και το πρόσημο στην αρχή, πχ το ---27 θα δώσει το -27. Επίσης αναγνωρίζει και αριθμούς με επιστημονική γραφή πχ 1.242ε-19, μπορούμε να έχουμε το e ή το ε για εκθέτη. Το διαχωριστικό δεκαδικών στον κώδικα είναι πάντα η τελεία. Στην κονσόλα εμφανίζεται με βάσει κάποιες ρυθμίσεις της Μ2000.

H IsExp συνάρτηση έχει και μια προαιρετική παράμετρο που λέμε ότι θέλουμε αριθμητική τιμή οπωσδήποτε. Αυτό σημαίνει ότι αν έχουμε αποτέλεσμα αντικείμενο θα πάρουμε την τιμή 0, και το αντικείμενο θα χαθεί. Στην  IsExp καλούμε άμεσα την IsExpA. Η δεύτερη μπορεί να βγει μετά την  ανάγνωση της πρώτης έκφραση γιατί μπορεί να βρήκε παρένθεση και μετά το κόμμα που σημαίνει ότι είναι αυτόματος πίνακας. Στους αυτόματους πίνακες (tuple) το (,) σημαίνει κενός πίνακας (είναι και αυτό μια έκφραση που βρίσκει το IsExp) ενώ το (1,) ή το ("α",) είναι αυτόματος πίνακας ενός στοιχείου. Δείτε ότι το "α" δεν είναι μέρος του IsExp. Όμως είναι του IsExpA γιατί αυτό κοιτάει αν έχουμε τελεστή  λογικό οπότε το "α">" " θα δώσει true (τιμή αριθμητική τύπου boolean, ισοδύναμη με το -1). Οι σταθερές true και false δεν είναι boolean, είναι αριθμητικές double. Οι λογικοί τελεστές (συγκρίσεων και πύλες όπως KAI, H, ΑΠΟ, ΌΧΙ) δίνουν boolean τύπο. Αν λοιπόν βρει αλφαριθμητικό και μετά το κόμμα σημαίνει ότι ανήκει σε αυτόματο πίνακα και κάνει έξοδο προς το IsExp. To IsExpA έχει όλους τους τελεστές και όποτε χρειάζεται έκφραση καλεί την IsExp, ή την logical(). Η logical() καλείται πρώτη γιατί αυτή καλεί την Number, αλλά και την IsStrExp (έχει δυο σετ για συγκρίσεις αλφαριθμητικών, ένα με κανονική σύγκριση βάσει δυαδικών ψηφίων, και ένα με σειρά γραμμάτων βάσει γλώσσας ελέγχοντας μια μεταβλητή mTextCompare (η Διακόπτες "+txt" αλλάζει σε σύγκριση βάσει γλώσσας.

Στο παράδειγμα που ακολουθεί, καλούμε το τμήμα ΔοκιμήΑ. Εκεί αδειάζουμε τον τρέχον σωρό, γιατί θέλουμε να είναι καθαρός εδώ. Φτιάχνουμε ένα τμήμα ΜΜ, και μετά δίνουμε εντολές ΜΜ με παραμέτρους. Δείτε ότι ο Διερμηνευτής δεν νοιάζεται για το τι θα στείλουμε. Θα στείλει ότι του ζητήσουμε. Στο τμήμα ΜΜ έχουμε μια εντολή που μας δείχνει το τρέχον σωρό τιμών, και μετά τον καθαρίζει.

Όταν αναγνωρίζεται ότι το ΜΜ είναι τμήμα, στο Execute καλείται η ProcModuleEntry. Αυτή το πρώτο πράγμα που κοιτάει είναι αν έχει ελεύθερο χώρο στη στοίβα επιστροφής. Αν δεν έχει θα τερματίσει με λάθος. Κατά την εκκίνηση του Διερμηνευτή γίνεται έλεγχος μεγέθους της στοίβας επιστροφής και αποφασίζει πόσες κλήσεις μπορεί να κάνει! Η στοίβα επιστροφής είναι διαφορετική από το σωρό τιμών της Μ2000, και είναι η βασική στοίβα που χρησιμοποιεί ο επεξεργαστής στη συγκεκριμένη υλοποίηση εκτέλεσης! Ο m2000.exe κατά την εκκίνηση έχει ζητήσει ένα μεγάλο μέγεθος στοίβας, και μετά συνδέει το m2000.dll. Μπορεί κάποιος να ανοίξει στο IDE της VB6 to m2000.vbp να τρέχει το dll, μέσα από το IDE και να εκτελέσει απλά το m2000.exe (που το φτιάχνουμε με το mexe.vbp). Αν στο IDE κατεβάσει στο αυστηρότερο το όριο σταματήματος σε λάθη Break On All Errors, τότε το πρώτο λάθος που θα βρει είναι η ανάγνωση του μεγέθους του Stack (πραγματικά η Μ2000 προκαλεί εσκεμμένο λάθος για να μετρήσει το μέγεθος, απλά έχει μια On Error για να το αποφύγει, το οποίο δεν λειτουργεί αν έχουμε το Break On All Errors (context menu, sub menu tongle).

Τμήμα ΔοκιμήΑ {
      Αδειασε
      Τμήμα ΜΜ {
            Σωρός
            Αδειασε
      }
      ΜΜ 100, "α"+"β"="αβ"
      ΜΜ "α"+"β"="αβ", 100
      ΜΜ 100,"αβ""α"+"β"
      ΜΜ "αβ"="α"+"β", 100
}
ΔοκιμήΑ


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

Συνάρτηση ΑφαίρεσεΑστέρια$(α$, &πόσα) {
      πόσα=μήκος(α$)
      α$=φίλτρο$(α$,"*")
      =α$
      πόσα-=μήκος(α$)
}
Τόσα=0
Τύπωσε ΑφαίρεσεΑστέρια$("***ΑΒ***ΓΔΕ***Ζ", &Τόσα), Τόσα

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

Αν το τμήμα στο οποίο βρίσκεται ο κώδικας είναι το Β τότε το &Τόσα θα είναι ένα αλφαριθμητικό με τιμή "Β.ΤΟΣΑ"  (θα έχουν φύγει οι τόνοι και θα έχουν γίνει κεφαλαία, επειδή έτσι δουλεύει τα ονόματα ο διερμηνευτής). Αυτό που μπήκε στο σωρό τιμών είναι η Ισχνή Αναφορά στο ΤΟΣΑ του Β.

Η Διάβασε ευθύνεται να πάρει μια ισχνή αναφορά και να την κάνει κανονική αναφορά. Πρώτα θα δεί το &πόσα που του λέει πάρε αναφορά για το πόσα. Αν η πόσα υπήρχε στο τμήμα θα έβγαινε λάθος, επειδή δεν επιτρέπεται δεύτερη αναφορά, ή αλλαγή αναφοράς.  Η εσωτερική συνάρτηση ReadMe δουλεύει για το Διάβασε. Μπορεί να ορίσει μεταβλητές, μπορεί να κάνει έλεγχο τύπου για αντικείμενα ή να δημιουργήσει νέες μεταβλητές με μετατροπή της τιμής στην επιθυμητή.


Συνάρτηση ΑφαίρεσεΑστέρια$ {
         Σωρός
      Διάβασε α$, &πόσα
      πόσα=μήκος(α$)
      α$=φίλτρο$(α$,"*")
      =α$
      πόσα-=μήκος(α$)
}
Τόσα=0
Βάλε ΑφαίρεσεΑστέρια$("***ΑΒ***ΓΔΕ***Ζ", &Τόσα)
Τύπωσε Γράμμα$, Τόσα


Αυτά για σήμερα!
Γιώργος Καρράς

Γράψτε κάποιο σχόλιο!

ΥΓ. Τεχνικά θέματα:
  1. Ο κώδικας είναι εδώ χρωματισμένος από τον διορθωτή της Μ2000.
  2. Όταν αντιγράφουμε κώδικα από το διορθωτή της Μ2000 φτιάχνονται δυο αντικείμενα στο πρόχειρο, ένα απλού Unicode κειμένου (UTF16), και ένα HTML κειμένου (UTF8). Στο δεύτερο έχουν μπει και οδηγίες (tags) για το χρώμα. Η επικόλληση στο blogger δίνει το HTML κείμενο, άρα αυτό με το χρώμα. Αν επιλέξουμε το απλό κείμενο θα πάρει το απλό κείμενο.
  3. Επειδή στο διορθωτή χρησιμοποιούμε είτε το TAB (εξορισμού το TAB) είτε το Διάστημα για να βάζουμε εσοχές, κατά την αντιγραφή σε HTML αν δει ο διερμηνευτής τον χαρακτήρα TAB τότε εισάγει οδηγίες για κείμενο Monospace (με ίδιο πλάτος γράμματος για κάθε γράμμα). Αυτό όμως στο Blogger δεν κάνει αναδίπλωση λέξεων, οπότε δεν βγαίνει μαζεμένο σε περίπτωση που έχουμε μικρό πλάτος σελίδας. Οπότε υπάρχει η οδηγία στη κονσόλα Διακόπτες "-nbs" όπου το nbs σημαίνει (non break space), και αλλάζονται όλα τα TAB (στην αρχή των παραγράφων με διαστήματα. Στην HTML το NBS το θέλουμε γιατί αλλιώς χάνουμε τις εσοχές εκτός και αν έχουμε ανοίξει το κείμενο για Monospace κείμενο!
  4. Το πλάτος του TAB το καθορίζουμε με τη εντολή Σ ή Συγγραφή ή Edit αλλά αντί για όνομα (που θα άνοιγε τον διορθωτή, δίνουμε θαυμαστικό και νούμερο. Έτσι το Σ ! 2 κάνει το TAB να εμφανίζεται με δυο χαρακτήρες (μόνο στον διορθωτή και στην εξαγωγή HTML όταν αλλάζουμε τα TAB εσοχής με NBS, δείτε στο παράδειγμα κάτω ότι έχουμε δυο διαστήματα στην εσοχή!).
  5. Αν δεν θέλουμε να χρησιμοποιούμε TAB τότε δίνουμε το διακόπτες "-tab" (η εντολή παίρνει πολλούς μαζί γι' αυτό είναι στον πληθυντικό. Πχ διακόπτες "-tab -nbs".
  6. Ακόμα και αν χρησιμοποιούμε TAB, μπορούμε με το shift TAB να βάλουμε εσοχή με διάστημα. Αν σε ένα μπλοκ δεν υπάρχει εσοχή με TAB αλλά υπάρχει έστω και μια εσοχή με διάστημα τότε αν μαρκάρουμε πολλές γραμμές το πλήκτρο TAB βάζει εσοχές με διαστήματα.
  7. Με το F10 στο διορθωτή αλλάζουμε την εμφάνιση, μια φορά δείχνει χαρακτήρες που δεν φαίνονται, όπως διαστήματα (μια ειδική τελεία) και TAB (βελάκι), NBS (παραμένει ως διάστημα), το διάστημα αριθμών, καθώς και το τέλος παραγράφου, ενώ μια άλλη φορά το κανονικό κείμενο. Και στις δυο εμφανίσεις μπορούμε να διορθώνουμε κείμενο!
Α$= "  "+χαρκωδ$(160)
Για ι=1 έως 3 {
  Τύπωσε Χαρκωδ(Μεσ$(Α$, ι)),
}
Τύπωσε  ' αλλαγή γραμμής


Μας δίνει:  32     8199   160
Κανονικά μπορούμε να βάλουμε το 160 με το shift+ctrl+space, και το διάστημα αριθμών με το alt+ctrl+space. Όμως αν βάλουμε το 160 (NBS) κατά την αντιγραφή από το blogger στον διορθωτή το 160 γίνεται 32. Οπότε εδώ το έφτιαξα με την χαρκωδ$() που γυρίζει τον Unicode χαρακτήρα που δίνουμε.






Τετάρτη 27 Οκτωβρίου 2021

Αναθεώρηση 36 (και 35) Έκδοση 10

Με αυτήν την αναθεώρηση τελείωσα τις αλλαγές, βελτιώσεις που ήθελα, και φυσικά λάθη που διορθώθηκαν.

Σκέφτομαι μια γενναία μεταμόρφωση της γλώσσας...αλλά θα δούμε στο μέλλον, πόσο θα μπορέσω να την κάνω!

Έκλεισα σήμερα τα 55 χρόνια (άλλα τόσα θέλω).



From Readme.txt (35 revision, 36 revision

Version 10 revision 35:

1. Fix const lambda to be used in a call by pass by reference

const b=lambda k=1 -> {

=k

k++

}

module dosomething (&k) {

Print k()

}

for k=1 to 10

dosomething &b

next

2. Final lambda in groups now can be used in a call when the group passed by reference.

group M {

final b=lambda k=1 -> {

=k

k++

}

}

module dosomething (&k, &z) {

Print k.b()  ' k.b() is the same as z()

Print z()

}

for k=1 to 5

dosomething &M, &M.b

next


3.Update Demo1 in Info



Version 10 revision 36:

1)A broken statement:

1. Fix the STOP statement. Now M1 module in info works fine.


2-4)Improvements:

2. Point now return 0x7FFFFFFF when we get color out of layer.

3. Gradient statement to Player layers preserve colour in non displayed pixels (transparent by a region on the window). So We can test if mouse pointer on a sprite layer return 0x7FFFFFFF (out) or the transparent color which we use at Player statement.

4. Module Sprites in info now has the new definitions (2 & 3) to handle better the displayed sprites using mouse.

Δευτέρα 25 Οκτωβρίου 2021

Revision 34 Version 10

From readme.txt 

1. Some fixes for using => immediate on a group as a result from a function

eg. Print FunctionReturnGroup()=>GroupMember

see module rev34ver10 in info file

2. New modules in info.gsb file

FUNNY, RECSOUND, REV34VER10

3. DD6 module in info file fixed.

4. Most modules in info checked again.

Module REV34VER10 in info.gsb:

module testA {
    Print @FunctionReturnGroup()=>Name()=>v$()="Ok"
    Print @FunctionReturnGroup()=>Name()=>val()=1234
    Print @FunctionReturnGroup()=>Name()=>prop=500
    Print @FunctionReturnGroup()=>Name()=>strProp$="yes"
    Print @FunctionReturnGroup()=>Name()=>strProp.key=1000
    function FunctionReturnGroup()
        local Z=random(1, 500)
        local group a {
            Property GroupMember {value}=Z
            Function Name {
                GROUP alfa {
                    function v$ {="Ok"}
                    function val {=1234}
                    property prop {value}=500
                    property strProp$ {value}="yes"
                    group strProp {
                        key=1000
                    }
                }
                =alfa
            }
        }
        =a
    end function
}
module testB {
    function FunctionReturnGroup() {
        Z=random(1, 500)
        group a {
            Property GroupMember {value}=Z
            Function Name {
                GROUP alfa {
                    function v$ {="Ok"}
                    function val {=1234}
                    property prop {value}=500
                    property strProp$ {value}="yes"
                    group strProp {
                        key=1000
                    }
                }
                =alfa
            }
        }
        =a
    }
    Print FunctionReturnGroup()=>Name()=>v$()="Ok"
    Print FunctionReturnGroup()=>Name()=>val()=1234
    Print FunctionReturnGroup()=>Name()=>prop=500
    Print FunctionReturnGroup()=>Name()=>strProp$="yes"
    Print FunctionReturnGroup()=>Name()=>strProp.key=1000
}
module testC {
    function FunctionReturnGroup() {
        Z=random(1, 500)
        group a {
            Property GroupMember {value}=Z
            Function Name {
                GROUP alfa {
                    function v$ {="Ok"}
                    function val {=1234}
                    property prop {value}=500
                    property strProp$ {value}="yes"
                    group strProp {
                        key=1000
                    }
                }
                =alfa
            }
        }
        =a
    }
    KK=FunctionReturnGroup()
    for kk {
         Print .Name()=>v$()="Ok"
         Print .Name()=>val()=1234
         Print .Name()=>prop=500
         Print .Name()=>strProp$="yes"
         Print .Name()=>strProp.key=1000
    }
    LL=FunctionReturnGroup()=>Name()
    for LL {
         Print .v$()="Ok"
         Print .val()=1234
         Print .prop=500
         Print .strProp$="yes"
         Print .strProp.key=1000
    }
}
module testD {
    KK=@FunctionReturnGroup()
    for kk {
         Print .Name()=>v$()="Ok"
         Print .Name()=>val()=1234
         Print .Name()=>prop=500
         Print .Name()=>strProp$="yes"
         Print .Name()=>strProp.key=1000
    }
    LL=@FunctionReturnGroup()=>Name()
    for LL {
         Print .v$()="Ok"
         Print .val()=1234
         Print .prop=500
         Print .strProp$="yes"
         Print .strProp.key=1000
    }
    function FunctionReturnGroup()
        local Z=random(1, 500)
        local group a {
            Property GroupMember {value}=Z
            Function Name {
                GROUP alfa {
                    function v$ {="Ok"}
                    function val {=1234}
                    property prop {value}=500
                    property strProp$ {value}="yes"
                    group strProp {
                        key=1000
                    }
                }
                =alfa
            }
        }
        =a
    end function
    
}
testA
testB
testC
testD


Κυριακή 24 Οκτωβρίου 2021

Αναθεώρηση 33 Έκδοση 10 (και λίγα λόγια για τις 31 και 32)

Αναθεώρηση 33

Μπορούμε να έχουμε συντομεύσεις που καλούν το m2000.exe, και να βάλουμε διακόπτες καθώς και ένα αρχείο. Πχ το -sbl που κάνει να εμφανίζονται με αριθμούς -1 και 0 οι λογικοί (boolean), μπορεί να είναι:

1. Mετά το όνομα του εκτελέσιμου (m2000.exe) , μπορούν να είναι αρκετοί διακόπτες όχι μόνο ένας. Οι διακόπτες είναι με - και με +, εδώ έχουμε το -sbl αλλά θα μπορούσαμε να είχαμε το +sbl. Δες παρακάτω στα περί της αναθεώρησης 31 τι παίζει!

μονοπάτι\m2000.exe -sbl

2. Mετά από συγκεκριμένο αρχείο που θέλουμε να ανοίγει η συντόμευση:

μονοπάτι\m2000.exe "μονοπάτιΑρχείου\αρειο.gsb" -sbl

Σε αυτήν την περίπτωση αν ρίξουμε πάνω στην συντόμευση άλλο αρχείο gsb θα παίξει αυτό που ρίξαμε με τους διακόπτες που έχουμε βάλει.

3. Μεταξύ εκτελέσιμου αρχείου και αρχείου που θέλουμε να ανοίξει το εκτελέσιμο, αν δεν έχουμε διαστήματα στο μονοπάτι δεν χρειάζονται τα εισαγωγικά. Τα Windows  10 βάζουν εισαγωγικά μόνο όταν υπάρχει διάστημα:

μονοπάτι\m2000.exe -sbl "μονοπάτιΑρχείου με διαστήματα\αρειο.gsb"

μονοπάτι\m2000.exe -sbl μονοπάτιΑρχείου\αρειο.gsb

Ισχύει το εξής:

Αν θέλουμε αντιγράφουμε σε ένα φάκελο τα m2000.exe και m2000.dll. Μπορούμε να έχουμε φτιάξει ένα πρόγραμμα και να το έχουμε σώσει έτσι: Σώσε myApp @, {StartModule: End}

Βάζουμε το myApp.gsb στο φάκελο, αλλάζουμε το m2000.exe σε myApp.exe και το m2000.dll αν θέλουμε σε lib.bin. Τώρα όταν ξεκινάμε το myApp.exe τρέχει αμέσως το MyApp.gsb (που είναι κρυπτογραφημένο) και αυτό γίνεται με άμεση σύνδεση με το τοπικό lib.bin (m2000.dll). Έτσι μπορούμε να έχουμε προγράμματα που ξέρουμε ότι δουλεύουν με μια έκδοση της Μ2000 και θα τρέχουν με αυτήν, ακόμα και αν έχουμε αλλάξει την έκδοση που έχει εγκατασταθεί τελευταία.

Υπάρχει τρόπος με την εντολή ICON να αλλάξουμε το εικονίδιο της εφαρμογής την ώρα που τρέχει το myApp.exe. Αλλά το καλύτερο είναι αν έχουμε την Vιsual Βasic 6, να ανοίξουμε το mexe.vbp και να αλλάξουμε το εικονίδιο της φόρμας frmAbout (απαιτείται τύπος ico, 8bit χρώμα, 16X16, 48X48, όχι png). Και πάλι χρειάζεται η εντολή ICON για να αλλάξει το εικονίδιο και το msgbox της M2000. Η διαφορά είναι ότι το αρχείο myApp.exe θα έχει το εικονίδιο της δικής μας εφαρμογής όχι της Μ2000.


Αναθεώρηση 32

Διορθώθηκε η δομή Επανέλαβε Πάντα (και Επανάλαβε Πάντα). Έπαιζε στην αγγλική έκδοση Do Always ή Repeat Always, αλλά στην ελληνική μόνο αν είχαμε τις αγκύλες (Επανέλαβε { } Πάντα ή Επανάλαβε {} Πάντα).

Αναθεώρηση 31

Εδώ έγινε μια δουλειά που είχα αφήσει στην Αναθεώρηση 30:

1. Πολύπλοκη χρήση των συναρτήσεων τύπου # για τους αυτόματους πίνακες ή  

Τμήμα ΤμήμαΈνα (α$){
      Πρόχειρο α$
}
ΤμήμαΈνα (("Μ","Ο","Σ","Δ")#Ταξινόμηση()#Γραφή$(", "),1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ","Ν")#Ταξινόμηση()#Γραφή$(", "))#Γραφή$("-")
Τύπωσε Πρόχειρο$


Ρου1((("Μ","Ο","Σ","Δ")#Ταξινόμηση()#Γραφή$(", "),1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ","Ν")#Ταξινόμηση()#Γραφή$(", "))#Γραφή$("-"))
Έλεγχος((("Μ","Ο","Σ","Δ")#Ταξινόμηση()#Γραφή$(", "), 1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ", "Ν")#Ταξινόμηση()#Γραφή$(", "))#Γραφή$("-")=Πρόχειρο$, (("Μ","Ο","Σ","Δ")#Ταξινόμηση()#Γραφή$(", "), 1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ", "Ν")#Ταξινόμηση()#Γραφή$(", "))#Γραφή$("-")=Πρόχειρο$)


Ρουτίνα Ρου1(α$)
      Πρόχειρο α$
Τέλος Ρουτίνας
Ρουτίνα Έλεγχος(α, β)
      Τύπωσε α, β
Τέλος Ρουτίνας

Έξοδος:

Δ, Μ, Ο, Σ-1-2-3-4-5-6-7-8-9-Ε, Ν, Τ, Χ

Αληθές Αληθές

Το Αληθές εμφανίζεται αν έχουμε το +SBL ως κρατημένος διακόπτης και αν έχουμε επιλογή GREEK στις Ρυθμίσεις  CTRL+U (υπάρχει και σαν εντολή και ρυθμίζει την γλώσσα των μηνυμάτων λάθους). Αν αλλάξουμε σε Latin θα δίνει True. Αν δώσουμε στη κονσόλα (ή με Θέσε μπροστά σε τμήμα) το Switches "-SBL" τότε θα πάρουμε -1 -1 (όπου το -1 είναι η αξία του Αληθές). H M2000 κοιτάει το 0 ως FALSE, ή Ψευδής ή Ψευδές, οπότε μια μη μηδενική τιμή είναι αληθής. Προσοχή στο Αν$() και Αν() (συναρτήσεις if() και if$()) όπου επειδή εκεί παίζει ρόλο το νούμερο θα πρέπει να δώσουμε σύγκριση (οι συγκρίσεις γυρνούν Boolean Λογικό). Πχ το if(k->1+1, 2+3, 3+5, 4+6)  αν το k=1 θα εκτελέσει μόνο την πρώτη παράσταση 1+1 (οι άλλες δεν θα εκτελεστούν). Στο if(k->100, 50) αν το k είναι -1 θα δώσει το 100 (αληθής), αν το k=0 θα δώσει 50 ψευδής, αλλά αν το k δεν είναι -1 και δεν είναι 0 τότε θα δώσει 0. Δηλαδή δίνει Αληθής μόνο όταν έχουμε -1 (ή boolean True που είναι το -1 σε αξία).

Με την εντολή Έλεγχος στην κονσόλα βλέπουμε τους διακόπτες (+ ενεργός - ανενεργός). Με Βοήθεια Διακόπτες βλέπουμε τι επιλογές έχουμε (εξηγούνται οι διακόπτες). Οι διακόπτες μπορούν να δοθούν στη λεγόμενη command line, δηλαδή κάνουμε μια συντόμευση του m2000.exe και γράφουμε. Οι αλλαγές θα είναι τοπικές για την συγκεκριμένη εκτέλεση.sbl. Φτιάχτηκε στην αναθεώρηση 33  η δυνατότητα να έχουμε τους διακόπτες στη command line (μέσω συντόμευσης) είτε πριν το όνομα είτε μετά.


2. Χρήση των #Συναρτήσεων για touple ή αυτόματους πίνακες, όταν επιστρέφονται ως τιμή ομάδας (αντικείμενο της Μ2000). Εδώ σε μερικές παραλλαγές, το αποτέλεσμα είναι 4 σε κάθε περίπτωση.

Κλάση αλφα {
      Αξια (χ) {
            =(1,2,3,4,5)#Μέρος(χ,3)
      }
}
ΜιαΛάμδα=Λάμδα κ=αλφα() (χ)-> {
      =κ(χ)
}


Τύπωσε ΜιαΛάμδα(1)#τιμή(2)


ΜιαΛάμδα=Λάμδα κ=αλφα() (χ)-> {
      =κ(χ)#τιμή(2)
}


Τύπωσε ΜιαΛάμδα(1)


Κλάση αλφα {
      Αξια {
            =(1,2,3,4,5)
      }
}


ΜιαΛάμδα=Λάμδα κ=αλφα() (χ)-> {
      =κ#Μέρος(χ,3)
}


Τύπωσε ΜιαΛάμδα(1)#τιμή(2)


ΜιαΛάμδα=Λάμδα κ=αλφα() -> {
      =κ
}


Τύπωσε ΜιαΛάμδα()#Μέρος(1,3)#τιμή(2)


Τύπωσε ΜιαΛάμδα() ' 1 2 3 4 5

Αναθεώρηση 30, Έκδοση 10

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

Αδειασε  '  Αδειάζει τον σωρό τιμών
// δυναμικά τμήματα και συναρτήσεις (μπορούν να αλλάξουν κώδικα, έχουν δικό τους χώρο θέασης)
Τμήμα ΤμήμαΈνα {
      Σωρός ' εμφανίζει τον σωρό στην οθόνη
      Αδειασε
}
Συνάρτηση ΜιαΣυνάρτηση {
      // []  ο σωρός αδειάζει και πηγαίνει σε ένα αντικείμενο
      // από το αντικείμενο τον μετατρέπουμε σε πίνακα!
      =Πίνακας([])
}


// Η λάμδα είναι πρώτης τάξεως συνάρτηση
// μπορεί να μπει σαν τιμή οπουδήποτε!
// επίσης έχει κλεισίματα, εδώ το κ που έχει έναν αυτόματο πίνακα (ή tuple)
ΜιαΛάμδα=Λάμδα κ=(1,2,3,4,5) (x) -> {
      =κ#Μέρος(x,3)
}


ΤμήμαΈνα ("Μ","Ο","Σ","Δ")#Τιμή$(Τυχαίος(0, 3)),1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ","Ν")#Τιμή$(Τυχαίος(0, 3))


//δίνει κάτι τέτοιο Σ123456789Ε
Τύπωσε (("Μ","Ο","Σ","Δ")#Τιμή$(Τυχαίος(0, 3)),1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ","Ν")#Τιμή$(Τυχαίος(0, 3)))#Γραφή$("")


Ρου1(("Μ","Ο","Σ","Δ")#Τιμή$(Τυχαίος(0, 3)),1,2,3,4,5,6,7,8,9, ("Τ","Ε","Χ","Ν")#Τιμή$(Τυχαίος(0, 3)))


Τύπωσε ΜιαΣυνάρτηση(1,2,3,4,5)#Ανάπ()#Γραφή$(", ") ' 5, 4, 3, 2, 1
Τύπωσε (1,2,3,4,5)#Ανάπ()#Γραφή$(", ") ' 5, 4, 3, 2, 1


Τύπωσε @ΚαιΆλλη(1, 2, 3)#Γραφή$("-") ' 1-2-3-2-1
Τύπωσε ΜιαΛάμδα(0)#Γραφή$("/") ' 1/2/3/4
Τύπωσε ΜιαΛάμδα(2)#Γραφή$("/") '3/4


// Στατικές ρουτίνες και συναρτήσεις (ο κώδικας είναι σταθερός, θέαση στο χώρο που ανήκουν)
Ρουτίνα Ρου1()
      Σωρός
      Άδειασε
Τέλος Ρουτίνας


Συνάρτηση ΚαιΆλλη(α, β, γ)
      =(α,β,γ,β,α)
Τέλος Συνάρτησης