Κυριακή 5 Ιουνίου 2016

Προχωρημένες καταστάσεις: 5% πιο γρήγορος ο διερμηνευτής!

Η έκδοση 8.1 (αναθεώρηση 28) είναι η τελευταία.

Τι νέο έχει η αναθεώρηση 28:
Ενώ είχα αποφασίσει να μην πειράξω άλλο τον κώδικα. Σκέφτηκα μια που τελείωσε...να δουλέψω προς την μεριά της βελτιστοποίησης.
Η ιδέα ήταν να αφαιρέσω αντικείμενα από το αντικείμενο εκτέλεσης που δεν χρησιμοποιούνταν άμεσα, και να τα προσθέτω κατ΄απαίτηση. Αυτό σημαίνει ότι στη ρουτίνες που θα δουλέψουμε με αυτό υπάρχει ένα If που κοιτάει αν το αντικείμενο είναι Nothing, και αν ναι τότε το φτιάχνει. Το να δούμε αν το αντικείμενο είναι  "τίποτα" είναι μια απλή σύγκριση, το τίποτα είναι το 0. Όμως αν το αφήσουμε να το φτιάχνει, τότε κάθε φορά που ξεκινάει ένα νέο αντικείμενο δεσμεύει χώρο, κάνει αρχικοποίηση και πάλι στο τέλος κάνει καθάρισμα. Ένα τέτοιο αντικείμενο είναι ο σωρός επιστροφής (ή στοίβα αν προτιμάτε). Ο σωρός χρησιμοποιείται σε ορισμένες περιπτώσεις  όπως στην κλήση ρουτινών (sub ...end sub), στο Για Επόμενο (όχι στο Για { } και για αυτό είναι πιο γρήγορο), καθώς και στο Gosub/return ρουτίνες δηλαδή χωρίς άμεσες, χωρίς παραμέτρους. Επιπλέον χρησιμοποιείται και όπου μας ενδιαφέρει ο προσωρινός χώρος μεταβλητών, γιατί ο σωρός κρατάει τις θέσεις των λιστών ονομάτων μεταβλητών και συναρτήσεων/τμημάτων, μόνο όμως σε ορισμένες περιπτώσεις. Όταν καλούμε τμήματα ή συναρτήσεις, δεν υπάρχει ειδικός σωρός αλλά ο κύριος σωρός του προγράμματος, εκεί που μπαίνουν οι αυτόματες μεταβλητές όπως λέμε. Για το λόγο αυτό και ο σωρός για τις ρουτίνες μπορεί να γίνει τεράστιος, ενώ ο κύριος σωρός είναι fix...και μάλιστα επειδή η M2000 είναι σε dll, το ύψος του σωρού καθορίζεται στο πρόγραμμα που την φορτώνει (εδώ την φορτώνει το m2000.exe, αλλά θα μπορούσε κάποιος να τη φορτώσει σαν script γλώσσα σε άλλη γλώσσα, είναι ένα αντικείμενο για τον υπολογιστή, και μπορεί κανείς να συνδέσει αναφορές σε αυτήν).
Μια άλλη ιδέα ήταν να αφαιρέσω την κύρια στοίβα ή σωρός τιμών, από το να ετοιμάζει νέο κάθε φορά που ξεκινάει το αντικείμενο εκτέλεσης, γιατί συνήθως ο σωρός δίνεται εκ νέου (οπότε πέταγε τον νέο που είχε δημιουργηθεί στην αρχή). Τέλος μια ακόμα αλλαγή έγινε στο σύστημα που κοιτάει τις αρχικές τελείες στα ονόματα. Εκεί έβαλα περιορισμό τις 10. Αποκλείεται κανείς λογικός να βάλει 5 τελείες, όχι δέκα. (σημαίνει ότι έχει ανοίξει πέντε αντικείμενα και κάθε αριθμός τελειών δηλώνει ένα από τα ανοικτά αντικείμενα). Τα αντικείμενα ομάδες μπορεί να είναι επώνυμα ή να ανώνυμα,και στην δεύτερη περίπτωση σημαίνει ότι δεν έχουν όνομα στο τμήμα που τα χρησιμοποιούμε αλλά θέση σε κάτι, όπως σε πίνακα ή κατάσταση ειδών. Για να τα ανοίξουμε, σημαίνει ότι θα δώσουμε όνομα, αλλά αντί να κάνουμε κάτι τέτοιο που θα έδινε ένα αντίγραφο, κάνουμε με άλλο τρόπο ένα προσωρινό άνοιγμα, όπου δίνει ο διερμηνευτής όνομα (μάλλον το έχει το κρυφό ήδη ενσωματωμένο), αλλά το σπουδαιότερο εδώ είναι ότι βγάζει τα μέλη της ομάδας από μια λίστα και τα βάζει σε κανονικό χώρο με το κρυφό όνομα.. Αντί λοιπόν να γνωρίζουμε το όνομα βάζουμε πριν τις ιδιότητες και τις μεθόδους την τελεία, ή τις τελείες αν έχουμε φωλιασμένα ανοίγματα.

Π.χ. αν στα Α(2) και Α(3) είναι δυο αντικείμενα, και έχουν από μια ιδιότητα Χ τότε μια Για ανοίγει αυτά τα δύο:
Κλάση Αλφα {
      Χ
}
Πίνακας Α(10)=Αλφα()
Α(2).Χ=5
Α(3).Χ=7
Για Α(2), Α(3) {
      Τύπωσε .Χ, ..Χ
      Άλλαξε .Χ, ..Χ
      Τύπωσε .Χ, ..Χ
}

Η άλλαξε (Swap) δεν μπορεί να ανοίξει τα αντικείμενα, το Άλλαξε a(2).X, A(3).X δεν δουλεύει, τα X δεν βρίσκονται σε λίστα μεταβλητών, οι τιμές τους είναι μέσα στο "πτητικό" αντικείμενο στο πίνακα. Για το λόγο αυτό πάει όπου θέλουμε. (η κύρια μνήμη που μπαίνουν οι τιμές μεταβλητών είναι γραμμική, και πάντα αυξάνεται και μειώνεται από το ένα άκρο, ποτέ δεν σβήνουν ή αφαιρούνται ενδιάμεσα. Τα επώνυμα αντικείμενα έχουν όλα τα μέλη ανεξάρτητα, και απλά κρατάνε μια λίστα για το ποια είναι όταν χρειαστεί να τα αντιγράψει.


H M2000 χρησιμοποιεί ένα αντικείμενο με αναφορά μόνο το οποίο είναι υποδοχέας άλλων. Ένα από αυτά τα άλλα είναι η Κατάσταση Ειδών, η απλά Κατάσταση. Αυτό το αντικείμενο καταχωρεί με ένα κλειδί (ένα νούμερο ή ένα αλφαριθμητικό, μοναδικό όμως)  διάφορα είδη, ακόμα και αντικείμενα. Μπορεί κανείς όμως, να καταχωρήσει το ίδιο! Ή να το καταχωρήσει αλλού, σε μια άλλη κατάσταση και αυτήν να την καταχωρήσει στην πρώτη. Μιλάμε για κυκλική αναφορά. το Α κρατάει το Β που αυτό κρατάει το Α. Όταν σβήνουμε αντικείμενα όπως αυτό με αναφορά, πρέπει να φύγει και η τελευταία για να καθαρίσει. Αν το Α δείχνει στο Β και το Β στο Α και καμία αναφορά από το πρόγραμμα στα Α και Β τότε απλά τα Α και Β μένουν στην μνήμη. Βέβαια στους σημερινούς υπολογιστές αυτό σημαίνει ότι μένουν στην μνήμη της εφαρμογής, και πως μόλις αυτή τερματίσει θα διαγραφούν, αλλά πώς; Άμεσα, χωρίς να σταλθεί μήνυμα ώστε τυχόν προετοιμασίες να συμβούν. Αυτό που πρόσθεσα είναι ένας τρόπος με την εντολή Clear η καθαρό, να διαγράψουμε  τέτοια αντικείμενα. Η διαγραφή γίνεται και πριν τερματίσει το πρόγραμμα ώστε σε περίπτωση που έχουμε αντικείμενα μέσα στη Κατάσταση να πάρουν και αυτά το μήνυμα "διαγραφή".
Ο μηχανισμός δουλεύει με μια λίστα όπου όταν πάμε να γράψουμε μια Κατάσταση σε μια Κατάσταση ή σε ένα πίνακα, τότε καταγράφεται σε αυτήν. Γράφεται μόνο μια φορά γιατί το κλειδί είναι ο δείκτης της και αυτός δεν αλλάζει., μέχρι να διαγραφεί. Οπότε κάθε φορά που είναι να καθαρίσουμε με την Clear η Καθαρό (χωρίς παραμέτρους καθαρίζει όλη τη μνήμη, αλλά δουλεύει μόνο από τη γραμμή εντολών), κοιτάει αν η λίστα έχει πράγμα και δίνει εντολή καθαρισμού πριν τα σβήσει. Δηλαδή οι καταστάσεις έχουν πια μια εντολή καθαρισμού. Με αυτό το τρόπο πρώτα καθαρίζει ο εσωτερικός πίνακας από κάθε αντικείμενο. Το σύστημα δεν βάζει ένα 0, αλλά ξέρει που είναι αντικείμενο και αφαιρεί 1 από τον μετρητή αναφορών. Λογικά πρέπει με το πρώτο πέρασμα να μείνουν μόνο οι αναφορές που έχει αυτή η λίστα. Οπότε μετά καθαρίζουμε και την λίστα. Μπορούμε να δώσουμε και δυο ή περισσότερες φορές αυτήν την εντολή.  Σε ένα πρόγραμμα που τρέχει άμεσα με διπλό κλικ το Clear το εκτελεί στο τέλος. Απλά όταν ασχολούμαστε με το να γράφουμε προγράμματα και να χρησιμοποιούμε αντικείμενα καλό είναι να καθαρίζουμε που και που την μνήμη, Σε ορισμένα λάθη ο διερμηνευτής προτείνει τον καθαρισμό.

Τελευταία Αναθεώρηση 27

Έκανα όλους τους δυνατούς ελέγχους, ο διερμηνευτής είναι μια χαρά!

1. Αφαίρεσα τον περιορισμό όπου ένα αντικείμενο Ομάδα δεν μπορούσε να είχε όνομα του τμήματος που το δημιούργησε. Παλαιότερα υπήρχε θέμα αλλά δεν υφίσταται πια.
2. Ξεκαθάρισα τις κλήσεις των συναρτήσεων και των τμημάτων (τρεις τρόποι).
στα τμήματα υπάρχει ο απλό τρόπος με το όνομα, μετά είναι με την Κάλεσε ή Call που δίνει αναδρομή, και ο άλλος λέγεται Κάλεσε Τοπικά όπου το όνομα του τμήματος αλλάζει στο όνομα του πατρικού, άρα μέσα στο τμήμα check3 τρέχουμε κώδικα σαν να είναι στο τμήμα που το καλεί (είναι σαν μια κλήση ρουτίνας αλλά εδώ γίνεται βάσει λίστας, πιο γρήγορα. Παραμένει  όμως η ρουτίνα ανώτερη για αναδρομή, επειδή δεν δημιουργεί αντικείμενο εκτέλεσης. Εδώ το τμήμα που καλούμε τοπικά δημιουργεί αντικείμενο εκτέλεσης. Όμως ειδικά στα τμήματα οι τυχόν στατικές δεν διατηρούνται με την κάλεσε (αν θέλουμε αυτή την λειτουργικότητα χρησιμοποιούμε συνάρτηση που το έχει)

\\ call module normal way
module check1 {
      print "ok"
}
check1
\\ call module with recursion
module check2 {
      read x
      if x>0 then call check2 x-1
      Print x,
}
check2 4
Print
\\ call module with local option
\\ now module see anythig
\\ but we need to read using new
module check3 {
      Read New M, b(), &k
      k++
      a(5)++
      Print ">>", b(5), a(5)
      z++
      check1
      check2 M
      Print
}
let m=1234, z=100, n=5
dim a(10)=2
call local check3 4, a(), &n
Print m,z, a(5), n
call local check3 4, a(), &n
Print m,z, a(5), n



Εδώ είναι το δοκίμιο για τις συναρτήσεις:

\\ call function normal way
function check1 {
      print "ok"
}
call check1()
\\ call function with recursion
function check2 {
      read x
      if x>0 then call check2(x-1)
      Print x,
}
call check2(4)
Print
\\ call function with local option
\\ now function see anythig
\\ but we need to read using new
function check3 {
      Read New M, b(), &k
      k++
      a(5)++
      Print ">>", b(5), a(5)
      z++
      call check1()
      call check2(M)
      Print
}
let m=1234, z=100, n=5
dim a(10)=2
call local check3( 4, a(), &n)
Print m,z, a(5), n
call local check3( 4, a(), &n)
Print m,z, a(5), n




Σάββατο 4 Ιουνίου 2016

Αναθεώρηση 26 -(τελευταίες...πινελιές)

Στην αναθεώρηση 26, έβαλα το ESC να σταματάει ένα While True {}, ένα Repeat {}  Always, ένα Do { } Until false, στο νορμάλ τρόπο εκτέλεσης.

Γενικά υπάρχουν 3 τρόποι εκτέλεσης κώδικα:
  1. Αργά
  2. Γρήγορα
  3. Γρήγορα !
Ο πρώτος εισάγει περισσότερο χρόνο για το λειτουργικό, οπότε γίνονται συχνότερα οι ανανεώσεις οθόνης και το διάβασμα του πληκτρολογίου
Ο τρίτος τρόπος δεν κάνει καμία ανανέωση οθόνης αν δεν την ανανεώσουμε με εντολή Refresh ή Ανανέωση.
Ο νορμάλ τρόπος κάνει ανανεώσεις επιλεκτικά (όχι τυχαία), βάσει ενός μετρητή. Μια Ανανέωση 1000 θα πει στο σύστημα να ανανεώνει κάθε 1 δευτερόλεπτο.

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

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

Επιπλέον έχω γράψει μερικές σημειώσεις για τον κώδικα, την χρήση τοπικών μεταβλητών και ρουτινών.

\\ Δοκιμές ταχύτητας
Φόρμα 80,40
\\ ορίζουμε τοπικές μεταβλητές - δεν είναι απαραίτητο, αλλά πρέπει να είναι στη αρχή του κώδικα
\\ ... και πάντως όχι σε επανάληψη.
Τοπικές α, ι, χ1,χ2,χ3, εμφάνιση=αληθές, επαναλήψεις=10000
\\ $(4,10), αναλογική γραφή, με χρήση στηλών 20 χαρακτήρων μη αναλογικών
Τύπωσε $(4, 10),
\\ εντός τμημάτων οι εντολές Αργά και Γρήγορα θέλουν το πρόθεμα Θέσε
\\ Το "Θέσε" στέλνει τις εντολές στο CLI, μεταφραστή γραμμής
      Θέσε Αργα
\\ οι ρουτίνες καλούνται ή με την Διαμέσου (Gosub) ή άμεσα
\\ Στα ονόματα των ρουτινών κεφαλαία-πεζά-τονισμένα είναι σημαντικά
\\ ... αυτό συμβαίνει γιατί είναι ετικέτες που αναζητούνται στο κώδικα άμεσα από το τέλος
\\ δεν ισχύει αυτός ο περιορισμός στις μεταβλητές και τα ονόματα οπουδήποτε αλλού
\\ ... αυτό συμβαίνει γιατί αυτά τα αναγνωριστικά βρίσκονται σε λίστα πάντα κεφαλαία
\\ και κάθε φορά που τα αναζητούμε, ο μετφραστής τα κάνει κεφαλαία, χωρίς τόνους και τα βρίσκει.
      Έλεγχος(&χ1)
      Θέσε Γρήγορα
      Έλεγχος(&χ2)
      Τύπωσε "Περίμενε λίγο, το επόμενο τεστ δεν κάνει ανανέωση οθόνης"
      Ανανέωση
      Θέσε Γρήγορα !
      Έλεγχος(&χ3)
      Θέσε Γρήγορα
      Οθόνη
Αποτελέσματα("Με")
      επαναλήψεις=30000
      Τύπωσε "Περίμενε λίγο, τα επόμενα τεστ δεν εμφανίζουν κάτι στην οθόνη"
      Ανανέωση
      εμφάνιση=ψευδές
      Θέσε Αργα
      Έλεγχος(&χ1)
      Θέσε Γρήγορα
      Έλεγχος(&χ2)
      Θέσε Γρήγορα !
      Έλεγχος(&χ3)
      Θέσε Γρήγορα
Αποτελέσματα("Χωρίς")
Πλάγια
\\ η εντολή αναφορά εμφανίζει κείμενο με στοίχιση (justification) με αναδίπλωση λέξης (έχει πολλές παραλλαγές)
\\ επιπλέον αν προκαλεί ολίσθηση τότε ενεργοποιείται  ένα σύστημα κράτησης μετά από ορισμένες ολισθήσεις για να δούμε το κείμενο
\\ και με το πάτημα του διαστήματος να πάμε στα επόμενα, πάλι μέρος του κειμένου.
Αναφορά {Ενδέχεται το Γρήγορα ! να είναι πιο αργό από το Γρήγορα, γιατί ο φόρτος εξαρτάται και από τα προγράμματα που τρέχουν στο περιθώριο και εδώ, στη δεύτερη σειρά δικιμών,  η διαφορά των δυο είναι μικρή και μπορεί να καλυφθεί από καθυστέρηση στο περιθώριο}
\\ η εντολή πλάγια δουλεύει ως διακόπτης, μπορούμε να δώσουμε και τιμή Πλάγια 1 και Πλάγια 0
\\ μπορούμε να γνωρίζουμε τη θέση του (δίνει 0 ή -1, ενώ δέχεται 0 ή μη μηδενική τιμή για -1, αληθές)
Πλάγια
Τέλος
\\ η εντολή τέλος (κάνει έξοδο) δεν χρειάζεται γιατί αν δει το Ρουτίνα ο διερμηνευτής θα κάνει αυτόματα έξοδο
\\ Στις ρουτίνες ότι φαίνεται στο κυρίως τμήμα, φαίνεται και στη ρουτίνα
\\ η α$ είναι τοπική, η &χ είναι τοπική και νέα κάθε φορά, ώστε να αποδίδεται νέα αναφορά.
\\ μπορούμε να έχουμε τοπικές μεταβλητές
Ρουτίνα Αποτελέσματα(α$)
      Αναφορά "Σχέση ταχύτητας Αργά-Γρήγορα-Γρήγορα!"
      Τύπωσε α$+" εμφάνιση"
      Τύπωσε   "Γρήγορα/Αργα", @(στήλη(2)), μορφη$( "{0:2:-10}%",(χ1-χ2)/χ1*100)
      Τύπωσε  "Γρήγορα !/Αργα",@(στήλη(2)), μορφη$( "{0:2:-10}%",(χ1-χ3)/χ1*100)
      Τύπωσε  "Γρήγορα !/Γρήγορα",@(στήλη(2)), μορφη$( "{0:2:-10}%",(χ2-χ3)/χ2*100)
Τέλος Ρουτίνας
Ρουτίνα Έλεγχος(&χ)
      Αν εμφάνιση Τότε Οθόνη
      Αναλυτής
      Για ι=1 έως επαναλήψεις {
            α=ι
       Αν εμφάνιση Τότε Τύπωσε α,
      }
      Αν εμφάνιση Τότε Τύπωσε
      χ=Φόρτος
      Αν εμφάνιση Τότε Τύπωσε χ
Τέλος Ρουτίνας




Αναθεώρηση 25 (αναγκαία και αυτή)

Λόγω της μετατροπής στο κώδικα για να δέχεται προαιρετικές παραμέτρους (optional), ένα "κόμμα" στο κώδικα για τα γεγονότα έκανε ζημιά σε ένα μέρος του συστήματος γεγονότων (όχι στα παράθυρα). Έκανα και ότι αναβαθμίσεις χρειάζονταν. Οπότε όλα καλά!

Θα επικεντρωθώ στο μικρό εγχειρίδιο της Μ2000 που γράφω παράλληλα.

Να και ένα μικρό δωράκι!
Μπορεί κανείς να χρησιμοποιήσει κλήσεις στο API των windows και να κάνει το εξής: Θα ανοίξουμε το Notepad των Windows μπροστά από την κονσόλα της Μ2000, και θα διαβάσουμε το τίτλο του παραθύρου του! Μετά θα φέρουμε τη κοσνόλα μπροστά από αυτό! Το hwnd σημαίνει Windows Handler και είναι ο αριθμός που χρειάζεται το σύστημα για να κινήσει το παράθυρο της κονσόλας (δεν τον αποθηκεύουμε αυτόν, και η μεταβλητή μόνο για ανάγνωση hWnd δίνει το τρέχον αριθμό)


Declare BringWindowToTop Lib "user32.BringWindowToTop" { Long hWnd }
Declare GetForegroundWindow Lib "user32.GetForegroundWindow" {}
Declare GetWindowTextLength Lib "user32.GetWindowTextLengthW" {LONG hwnd}
Declare GetWindowText Lib "user32.GetWindowTextW" { Long hWnd, &c$, long c }
AAA$=string$(chr$(0), 502)
cch=500
wait 2000
win {notepad.exe "c:\Hello1234.txt"}
wait 2000
refresh
M=GetForegroundWindow()
if GetWindowTextLength(M)>0 then {
kk= GetWindowTextLength(M)
try {
      zz= GetWindowText(M, &aaa$, cch)
      Print zz, cch, leftpart$(aaa$,0)
      Print "Exit ok"
      }
}
Print "ok1"
refresh
wait 500
Print BringWindowToTop(hwnd)
wait 500
print "back"
refresh
wait 500

print "ok"


Παρασκευή 3 Ιουνίου 2016

Δημιουργία και Εκτέλεση C ρουτινών από τη Μ2000


Στην Έκδοση 10 στο αρχείο info υπάρχει ο κώδικας ανανεωμένος με υποστήριξη για ζήτηση άδειας για την εγγραφή στο   C: σε Windows 10. (εκτελεί το cmd σε elevated, για διαχειριστές, αφού πάρει την άδεια από το χρήστη). Αν επιλέξουμε άλλο φάκελο μέσα στον C: πχ C:\Test δεν έχουμε θέμα.

Το παρακάτω πρόγραμμα το γράφουμε σε ένα αρχείο gsb. Μπορούμε από τη Μ2000 να γράψουμε:
Σ "cdll.gsb"
(όταν το όνομα αρχείου είναι με εισαγωγικά στη Συγγραφή ή Σ  ή Edit τότε διορθώνουμε αρχείο στο δίσκο, δηλαδή με την έξοδο από τον διορθωτή, το αρχείο θα σωθεί, και δεν θα υπάρχει στην μνήμη)
μετά φορτώνουμε με αυτό: (Φόρτωσε ή Load)
Φόρτωσε cdll

και με A (λατινικό) ξεκινάει!

Προϋπόθεση για να λειτουργήσει είναι να υπάρχει ο gcc ο compiler της C  (MinGW)
Αυτόν χρειαζόμαστε:
gcc compiler

(γενικά μετά την εγκατάσταση από τον Installer θα χρειαστεί να πάμε στις ιδιότητες του υπολογιστή και "για προχωρημένους" να βρούμε το κουμπί "environment variables" και στο Path προσθέτουμε το που βρίσκεται ο gcc.exe - θα μπορούσαμε να βάλουμε το μονοπάτι απευθείας στις DOS εντολές του προγράμματος εδώ. Το μονοπάτι το προσθέτουμε βάζοντας το ερωτηματικό ; και μετά το μονοπάτι πχ C:\MinGW\bin)

Ο περισσότερος κώδικας του προγράμματος είναι για να φορμάρουμε την εξαγωγή στην οθόνη,

Με λίγα λόγια:
Φτιάχνουμε ένα dll με τη C με δυο συναρτήσεις. Η μια παίρνει έναν πίνακα Long τον οποίο τον φτιάχνουμε με Buffer (Διάρθρωση) και τον δίνουμε με δείκτη. Το dll έχει φορτωθεί και η κλήση στη συνάρτηση μας γεμίζει τον πίνακα. Η δεύτερη συνάρτηση μας επιστρέφει ένα γινόμενο.
Για να αλλάξουμε το πρόγραμμα στη C πρέπει να δηλώσουμε NEW δηλαδή νέο πρόγραμμα για να βγάλει ο διερμηνευτής την βιβλιοθήκη εκτός, οπότε θα την φορτώσει όταν θα ζητηθεί (διαφορετικά θα χρησιμοποιεί την φορτωμένη).
 Αν βγει λάθος από τον compiler, δεν γυρίζει μήνυμα ειδικό αλλά μας ενημερώνει το πρόγραμμα (επειδή δεν θα υπάρχει το object αρχείο)
Έχω δώσει 1000 χιλιοστά του δευτερολέπτου (1 δευτερόλεπτο) για να εκτελεστεί η εντολή DOS. μετά γυρνάει η ροή στο πρόγραμμα. Αν χρειαστεί το αυξάνουμε!

Δεν υπάρχει σύγκριση, στη C έχουμε αποτέλεσμα σε dt...


Module A {\\ empty the stack
\\ clear values
Refresh 30
Flush : Clear
Fkey 8, {Edit TestMe}
Fkey 9, {Edit c()}
Fkey 10, {Edit a}
Fkey 4, {Flush : TestMe 10000}
Fkey 5, {Save Command$ : Push Command$: New : Cls,0 : Load Letter$ : A}
Clear
Pen #A7CC99
Cursor 0,0
Gradient #738493,#434453
Double
Mark : Print $(4),@(1)," Make a dll and run it - Using gcc from M2000 Interperter"
Normal
      Data "F4","Run 10000","F5","Unload Lib - Compile - Load Lib - Run", "Ctrl+A","Save Program"
      Data "F8","Edit Test M2000 code","F9","Edit C code","F10","Edit Main code"
      c=9
      For i=1 to 3 { Keys(c,0,"-  ") : c++}
      Cursor 0,row-3
      For i=1 to 3 { Keys(c, width/2, "-  ") : c++}
      there=row
Cls #636473, there
\\ Scroll Split there+3
Cls #434453, there+3
\\ a  c program is written in C()
A$= &C()
A$=mid$(A$,2,len(A$)-2)
F=1

Dos "del  c:\adll*.*", 1000;
\\ alt "+" "2" "7" "1" "3" we get ✓
\\ M2000 has unicode internal custom editor
keys(14, 0,"", "✓","Old files Deleted")
open "c:\adll3.c" for output as F
Print #F, A$
close #F
keys(14, 0,"", "✓","Export c:\adll3.c")
dos "cd c:\ && gcc -c -DBUILD_DLL "+"adll3.c", 1000;
if exist("c:\adll3.o") then {
      keys(14, 0,"" ,"✓","Object file complete c:\adll3.o")
      dos "cd c:\ && gcc -shared -o "+"adll3"+".dll "+"adll3"+".o -Wl,--out-implib,libmessage.a", 1000;
} else Print"Error" :exit
if not exist("c:\adll3.dll") then Error "No dll error"
keys(14, 0,"" ,"✓","Linked with gcc c:\adll3.dll")
Push row

Cursor 0,there
\\ Results
dir c:\
Pen 15 {
      keys(14, 0,"" ,"Directory", Dir$)
      menu \\ clear internal menu list
      \\ ! sort by name, + not display just feed menu list
      files ! + "adll3.*"
      If menuitems=0 then break
      For i=1 to menuitems
      cursor 0,row+(i mod 2 =0)
      keys(14, 0-(width/2)*(i mod 2 =0),"" ,str$(i),menu$(i))
      Next i
      if (i mod 2) then print
}
\\ set default directory
Dir User
Cursor 0,Number 'pop row
Print "Perform Test (Y/N):";
Refresh
Clear Yes, No
Repeat {
      Yes=keypress(89)
      No=Keypress(78)
      wait 10
} Until Yes or No
Flush
If Yes then { TestMe } else print "N"
Pen 14

Sub Keys(c, x,pre$)
local t=tab
            Pen c { \\ change Pen only for here, ~(15) change again to white
                  \\ use right justify
                  Print @(x),$(7,6),letter$,
                  italic 1
                        \\ use normal left justify and restore column width
                        Print @(x+6),$(4,t),~(15), pre$+letter$
                  italic 0
            }
            If c>13 then Refresh
End Sub
}
Function C {/* C Code for DLL
*  from an example in M2000
*  Using gcc from http://www.mingw.org/
*  gcc -c -DBUILD_DLL adll3.c
*  gcc -shared -o adll3.dll adll3.o -Wl,--out-implib,libmessage.a
*/
long add2(long a[], long b)
{
      long i, m;
      m=a[0]+2;
      for (i=0;i<b;i++)
                  a[i]=i+m ;
         return 0;
}

long mult(long num1, long num2)
{
   long product;
   product = num1 * num2;
   return product;
}

}
Module TESTME {Pen #A7CC99
max%=200
Read ? max%
\\ we make a buffer
if max%<20 or max%>1000000 then exit
Print Over $(6),"Test for array of"+str$(max%)+" long"
Refresh
Buffer clear alfa as long*max%

\\ Feed unsign 100 to alfa[0]
Return alfa, 0:=100

\\ Now we load lib (lib loaded once)
Declare add2 lib c "c:\adll3.add2" {long c, long d}
Declare mult lib c "c:\adll3.mult" {long c, long d}

\\ We pass alfa(0) as the first long in buffer
Print mult(12,4)

\\ read from memory alfa(0)
\\ and populate array with values
\\ now we get values in buffer (we use as a Peek() this: Eval(<buffer>, index as long))
clear m, m1
Profiler
if add2(alfa(0), Max%) else {
      m=Timecount
      Print eval(alfa,Max%-1), m
}

Dim a(Max%)
Profiler
b=100+2
For i=0 to Max%-1 {
      a(i)=i+b
}
m1=TimeCount

Print a(Max%-1), m1
if m<2 then m=1
Print format$("C is by far the fastest language {0}% from M2000", m1/m*100)

Print Over $(6), "Test 20 Random Items"
Print Under
Equ$=string$("0",log(max%)+1)
Document doc$
Inventory pack
\\ choose one of two methods
If max%<100 then {
\\ use  a populated pack, remove used

for i=0 to max%-1: Append pack, i:=i: next i
            L=Lambda pack -> {
                  if len(pack)=0 then error "empty"

                  k=random(1, len(pack))-1
                  \\ return value (key as value, when no value exist) using k as index
                   =pack(k!)
                                     try {
                    Delete pack, eval$(pack, k)
                    }
                    if error then {
                    cls
                    For i=0 to len(pack)-1
                    print i, pack(i!)
                    next i
                    test
                    a$=key$
                    }
                 
            }
} else {
\\ use an empty pack, store used
      L=Lambda pack, max% -> {
       repeat {
              k=random(0, max%-1)
        } until not exist(pack, k)
        \\ so now k exist, for the next time
        append pack, k
        =k
}
}
For i=1 to 20 {
      k=L()
      doc$= format$("{0}) {1}={2} ({3})",str$(k+1, equ$), eval(alfa, k), a(k), str$(eval(alfa, k)=a(k),{"Not Equal";"Equal";"Not Equal"}))
      if i<20 then {
                  \\ just a new line
                  doc$={
                  }
      }
}
for i=1 to 5 { print}
X=row-5

\\ sorting
Sort Doc$
refresh 1000
For i=1 to 4
      Print @(width*((i-1)/4),X),
      Report 2, doc$, Width/4, 5 Line 5*i-4
Next i
Print
Refresh 30
Pen 14
}

Αναθεώρηση 24 (σχεδόν τελική αναθεώρηση)

Διορθώθηκε το πρόβλημα στην ανάγνωση ονομάτων συναρτήσεων στα Com αντικείμενα!
Το παρακάτω στο 14 περίπου έβγαζε out of stack. (το alfa είναι Inventory, λίστα της Μ2000 που λέγεται Κατάσταση)

Declare Worksheet "{00020820-0000-0000-C000-000000000046}"
Profiler
FOR I=1 TO 100 {
alfa=param(Worksheet)
Print i
}
? timecount


Διορθώθηκαν κάποιες αλλαγές που είχε επιφέρει η χρήση των πινάκων κατακερματισμού για εύρεση ονομάτων, κύρια με την αναδρομή:

παράδειγμα:
function k {
      function mp {
            read x
            if x<2 then =1 : exit
            =mp(x-1)*x
      }
      read ka

  = mp(ka)
}
Print k( 10 )


function k$ {
      function mp$ {
            read ? x
             if x<2 then ="1" : exit
            =str$(val(mp$(x-1))*x)
      }
      read ka
  = mp$(ka)
}
Print k$( 10 )

Στις λάμδα συναρτήσεις μπήκαν το Λάμδα$() και Λάμδα() για να γίνεται αναδρομή χωρίς να μας ενδιαφέρει το "τρέχον" όνομα. Στο παράδειγμα γίνεται αναδρομή στο στοιχείο πίνακα Α(3) που έχει μια λάμδα συνάρτηση (και έτσι δεν έχει όνομα, μόνο θέση σε πίνακα).
Γίνεται κλήση του CheckMe με δυο τρόπους. η Κάλεσε ή Call το καλεί με δυνατότητα αναδρομής, ενώ η κλήση χωρίς αυτήν, με το όνομα μόνο δεν δίνει δικαίωμα αναδρομής (έχει ρόλο το όνομα που χρησιμοποιείται τη δεδομένη στιγμή και στο Call αλλάζει εσωτερικά)

Module CheckMe {
      Group z {
            p=lambda-> {
                  read n
                  if n<2 then =1 : exit
           
                  =lambda(n-1)*n
            }
      }
      Print z.p(3)
      m=z.p
      Print m(6)
      Print M(12)
      dim A(10)
      A(3)=m
      Print A(3)(8)
}

CALL CheckMe
CheckMe