Κυριακή 6 Ιανουαρίου 2019

New Primes Algorithm

This program first publish here
Use Revision 20 (bug on ELSE <number labels> fixed)

Now we can make a bigger computation using Fast! mode which eliminate Gui/console refresh to gain speed. We can make a refresh each 50primes, so we have a tiny delay on refreshing if we move a window above M2000 console.
Also I change IsPrime to not add to Inventory Known1, because we want this inventory to have all primes without any missing until the last one.
I provide another IsPrime2 which use PrimeNth to add to Known1.
Inventories are reference type. Lambda functions are value type, but closures which are reference type copied the reference by value, so we get then by reference. Inventories start now with 3 known primes, and PrimeNth works for odd numbers only (see x+=2 before the loop statement)
Loop statement check a flag in a block of code ({ }), so when the block ends restart again (resetting the loop flag)


Module CheckPrimes {
      \\ Inventories are lists, Known and Known1 are pointers to Inventories
      Inventory Known=1:=2@,2:=3@,3:=5@
      Inventory Known1=2@, 3@, 5@
      \\ In a lambda all closures are copies
      \\ but Known and Know1 are copies of pointers
      \\ so are closures like by reference
      PrimeNth=lambda Known, Known1 (n as long) -> {
            if n<1 then Error "Only >=1"
            if exist(known, n) then =eval(known) : exit
            if n>5 then {
                 i=len(known1)
                 x=eval(known1, i-1)+2
            } else x=5 : i=2
            {
                  if i=n then =known(n) : exit
                  ok=false
                  if frac(x) then 1000
                  if frac(x/2) else 1000
                  if frac(x/3) else 1000
                  x1=sqrt(x) : d=5@
                  Repeat
                        if frac(x/d ) else exit
                        d += 2: if d>x1 then ok=true : exit
                        if frac(x/d) else exit
                        d += 4: if d<= x1 else ok=true: exit
                   Always
      1000 If ok then i++:Append Known, i:=x : if not exist(Known1, x) then Append Known1, x
                   x+=2 : Loop }
      }
      \\ IsPrime has same closure, Known1
      IsPrime=lambda Known1 (x as decimal) -> {
            if exist(Known1, x) then =true : exit
            if Eval(Known1, len(Known1)-1)>x then exit
            if frac(x/2) else exit
            if frac(x/3) else exit
            x1=sqrt(x):d = 5@
            {if frac(x/d ) else exit
                  d += 2: if d>x1 then =true : exit
                  if frac(x/d) else exit
                  d += 4: if d<= x1 else =true: exit
                  loop
             }
      }
      \\ fill Known1, PrimeNth is a closure here
      IsPrime2=lambda Known1, PrimeNth (x as decimal) -> {
            if exist(Known1, x) then =true : exit
            i=len(Known1)
            if Eval(Known1, i-1)>x then exit
            {
                  z=PrimeNth(i)
                  if z<x then loop else.if z=x then =true :exit
                  i++
            }
      }
      Print "First twenty primes"
      n=PrimeNth(20)
      For i=1 to 20 : Print Known(i),: Next i
      Print
      Print "Primes between 100 and 150:"
      c=0
      For i=100 to 150
            If IsPrime2(i) Then print i, : c++
      Next i
      Print
      Print "Count:", c
      Print "Primes between 7700 and 8000:"
      c=0
      For i=7700 to 8000
            If IsPrime(i) Then print i, : c++
      Next i
      Print
      Print "Count:", c
      Print "200th Prime:"
      Print PrimeNth(200)
      Print "List from 190th to 199th Prime:"
      For i=190 to 199 : Print Known(i), : Next i
      Print
      Print "Wait"
      Refresh ' because refresh happen on next Print, which take time
      ' using set fast we get no respond from GUI/M2000 Console
      ' also Esc, Break and Ctrl+C not work
      ' we have to use Refresh each 500 primes to have one Refresh
      Set fast !
      for i=500 to 10000 step 50: m=PrimeNth(i): Print "."; :Refresh:Next i
      Print
      Print "10000th Prime:", PrimeNth(10000)
      ' reset speed to fast (there are three levels: slow/fast/fast!)
      set fast
      Print
      Rem 1 : Print Known
      Rem 2: Print Known1
}
CheckPrimes









First twenty primes
 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71
Primes between 100 and 150:
 101 103 107 109 113 127 131 137 139 149
Count: 10
Primes between 7700 and 8000:
 7703 7717 7723 7727 7741 7753 7757 7759 7789 7793 7817 7823 7829 7841 7853 7867 7873 7877 7879 7883 7901 7907 7919 7927 7933 7937 7949 7951 7963 7993
Count: 30
200th Prime: 
 1223
List from 190th to 199th Prime:
 1151 1153 1163 1171 1181 1187 1193 1201 1213 1217
Wait
.... (truncate for output)
10000th Prime: 104729
 



And this is the code for optimization for PrimeNth()


PrimeNth=lambda Known, Known1 (n as long) -> {
      if n<1 then Error "Only >=1"
      if exist(known, n) then =eval(known) : exit
      if n>5 then {
           i=len(known1)
           x=eval(known1, i-1)+2
      } else x=5 : i=2
      {
            if i=n then =known(n) : exit
            if frac(x) then 999
            if frac(x/2) else 999
            if frac(x/3) else 999
            x1=sqrt(x) : d=5@
            {if frac(x/d ) else 999
                  d += 2: if d>x1 then 1000
                  if frac(x/d) else 999
                  d += 4: if d<= x1 else 1000
                  loop
             }
999 x++ : Restart
1000 i++:Append Known, i:=x : if not exist(Known1, x) then Append Known1, x
         x++ : Loop }
}


Σάββατο 5 Ιανουαρίου 2019

Revision 20 Version 9.6

1) Fix statement execution  out of order in Control Form (forget to set a reference to output object, preventing to display with Print statement)
2) Fix number to GOTO after ELSE statement.
Now this test program print "ok"

if false else 500
Print "fault"
Exit
500 Print "ok"



Παρασκευή 4 Ιανουαρίου 2019

Αναθεώρηση 19 Έκδοση 9.6

Δυο μέρες εργασία! Σε αυτήν την αναθεώρηση ήθελα και πέτυχα να δουλεύουν τα LOOP, RESTART, BREAK,  CONTINUE, EXIT παντού. Ελληνικές εντολές αντίστοιχα είναι τα ΚΥΚΛΙΚΑ, ΞΕΚΙΝΑ, ΔΙΕΚΟΨΕ, ΣΥΝΕΧΙΣΕ, ΕΞΟΔΟΣ.

Σε ένα μπλοκ κώδικα μπορούμε να δώσουμε το Έξοδος για να συνεχίσουμε μετά από αυτό. Επίσης μπορούμε να δώσουμε το Διέκοψε για να διακόψουμε και όλα τα μπλοκ που φωλιάζουν το τρέχον. Επίσης με το Κυκλικά κάνουμε για μια φορά το μπλοκ να επαναληφθεί όταν φθάσουμε στο τέλος του. Αν κάθε φορά βρίσκει ο διερμηνευτής την εντολή αυτή, θα κάνει συνέχεια επαναλήψεις (κύκλους). Κάθε μπλοκ έχει μια σημαία για να κάνει ή όχι επανάληψη μετά την τελευταία εντολή. Κάθε φορά που ξεκινάει το μπλοκ κώδικα η σημαία γίνεται ψευδής. Έτσι αν την γυρίσουμε σε αληθής, με την Κυκλικά θα πετύχουμε την επανάληψη.
Ορισμένα μπλοκ κώδικα κάνουν επανάληψη γιατί ανήκουν σε δομές με επανάληψη, όπως την ΓΙΑ (FOR), την ΕΝΩ (While) την Επανέλαβε (REPEAT ή DO). Σε αυτά καθώς και στα μπλοκ που σημαδέψαμε για επανάληψη, η Συνέχισε κάνει άμεση επανάληψη, χωρίς να φθάσει στο τέλος. Η ίδια εντολή, σε ένα μπλοκ που  δεν έχει σημανθεί για επανάληψη κάνει έξοδο.  Τέλος η Ξεκίνα κάνει επανάληψη άμεσα, αδιαφορώντας για τη σημαία επανάληψης. Η Συνέχισε πότε λειτουργεί σαν Έξοδος (σημαία επανάληψης ψευδής) και πότε σαν Ξεκίνα (σημαία επανάληψης αληθής).

Εκεί που μπλέκονται τα πράγματα είναι στην δομή ΕΠΙΛΕΞΕ ΜΕ (SELECT CASE). Αυτή η δομή έχει σχεδιαστεί για να λειτουργεί με και χωρίς μπλοκ κώδικα. Σε κάθε Με (Case) ή στο τελευταίο Αλλιώς Με ή (ELSE CASE) μπορούμε να βάλουμε μια γραμμή εντολών ή ένα μπλοκ εντολών με πολλές γραμμές ανάμεσα. Ο διερμηνευτής δέχεται το Αλλιώς και χωρίς το ΜΕ αλλά θα υπάρχει πρόβλημα αν είναι φωλιασμένο σε μια Αν Τέλος Αν ( δομή χωρίς μπλοκ κώδικα), επειδή και αυτή έχει μια Αλλιώς. Για συμβατότητα με τα παλιά προγράμματα η τωρινή έκδοση δέχεται και το παλιό τύπο με την απλή Αλλιώς. Στην νέα αναθεώρηση μπορούμε να βάζουμε πολλές εντολές σε μια γραμμή μετά την Με ή την Αλλιώς Με, ενώ στις παλαιότερες εκτελούνταν μόνο μια και οι τυχόν άλλες στην γραμμή απορρίπτονταν.Επίσης μπορούμε να βάζουμε δομή Αν σε μια γραμμή με όλα τα καλά της Αλλιώς.Αν και Αλλιώς, με ή χωρίς μπλοκ κώδικα (αρκεί τα μπλοκ να είναι εντός γραμμής).

Στη περίπτωση λοιπόν που έχουμε μια γραμμή κώδικα και όχι μπλοκ, οι εντολές για τα μπλοκ, όπως είδαμε παραπάνω, μπορούν να εκτελεστούν σε αυτή και θα επηρεάσουν το μπλοκ που ανήκει η ΕΠΙΛΕΞΕ ΜΕ (παλαιότερα μόνο τα ΔΙΕΚΟΨΕ και ΕΞΟΔΟΣ λειτουργούσαν, ως απλές εντολές μόνο, και όχι σε κάποια δομή Αν με πολλά μέρη (σε μια γραμμή εδώ).

Οι εντολές Διέκοψε και Συνέχισε σε μπλοκ μετά την Με κάνουν άλλη δουλειά. Ενώ θα περίμενε κανείς ότι η Διέκοψε διακόπτει το μπλοκ, εδώ κάνει κάτι άλλο: Διακόπτει τον έλεγχο συνθηκών στην ΜΕ (CASE) και εκτελεί όλα τα ΜΕ (χωρίς να εκτελεί τις συνθήκες), μέχρι ή να τελειώσουν (δεν εκτελεί της Αλλιώς Με) ή μέχρι κάποιο μπλοκ να εκτελέσει τη Συνέχισε η οποία κάνει έξοδο από την δομή ΕΠΙΛΕΞΕ ΜΕ (υπάρχει και ως ΕΠΕΛΕΞΕ ΜΕ).

Υπήρχε μεγάλη δυσκολία για να προγραμματιστεί σωστά ο διερμηνευτής, διότι στις Αν χωρίς μπλοκ σε μια γραμμή ουσιαστικά εκτελεί μπλοκ εσωτερικά και τα "σινιάλα" από τις παραπάνω πέντε εντολές πρέπει να μεταβιβάζονται στο πιο πάνω μπλοκ, και αν έχουμε την Επίλεξε ακριβώς από πάνω, τότε έχουμε τριπλό πέρασμα, μια στη ΑΝ, μια στην ΕΠΙΛΕΞΕ και μια στο μπλοκ που θέλουμε να ισχύουν αυτά. Παράλληλα διόρθωσα και την εμφάνιση κώδικα στη φόρμα Έλεγχος, ώστε να συμβαδίζει ο χρωματισμένος κώδικας και η σημειωμένη τρέχουσα εντολή σε περίπτωση της Επίλεξε Με.

Το παρακάτω δείχνει τη λειτουργία των BREAK/CONTINUE στην SELECT CASE. Θα ανοίξει η φόρμα Έλεγχος και εμείς πατάμε το Next Step και βλέπουμε τις εντολές καθώς εκτελούνται. Θα μας τυπώσει τα break, me, 1000, bad και ok. Αλλάξτε την Print "me" με την Exit. Θα πάρετε τα break και ok. Αλλάξτε την Exit με την Break, τώρα θα πάρετε μόνο το break, γιατί θα γίνει έξοδος από το τμήμα alfa.

Module alfa {
      Test "this"
      {
            select case true
            case true
            {print "break" : break}
            case 50
            Print "me"
            case 100
            { Print 1000 : continue}
            case 500
                  Print "nothing" : Goto 100
            end select
            Print "bad"
            exit
      }
      100 Print "ok"
}
alfa


Εκτός από τις Έξοδος και Διέκοψε (Exit & Break) υπάρχει και η Προς ή Goto για έξοδο σε προκαθορισμένο σημείο (μπορεί να γίνει προς οποιαδήποτε πλευρά αλλά όχι εκτός χώρου τμήματος/συνάρτησης. Δοκιμάστε μερικές αλλαγές, πχ το 900 αντί του 9, ή το break στο Case 4 (σε αυτή την αναθεώρηση μπορούμε να τα βάλουμε και τα δυο χωρίς μπλοκ, αρκεί να είναι σε μια γραμμή). Επίσης η Προς λειτουργεί χωρίς μπλοκ, αν θέλουμε, και με δομή Αν.

module zz {
      test "me too"
      {
      a=9 '  900
      here:
      a-=4
      Print "Last Test"
      Select case A
      Case 1
            if true then exit else Print "ok"
      Case 1.5
            break
      Case 2
            {if true then break}
      Case 4
            Print "case 4"   ' : break
      Case 5
      {      
            goto here
      }
      Else
      {
            goto finish
      }
      End Select
      print "no error"
      }
      print "end"     
      finish:
      ? "done"
}
zz
? "end zz"


Στο παρακάτω παράδειγμα η εντολή Print "ok" θα εκτελεστεί είτε είχαμε true είτε false στο If. Αν δεν είχαμε σε μπλοκ τα δυο Print, τότε όλη η γραμμή θα ελεγχόταν από το If. Κάντε αλλαγές! Πχ τι θα αλλάξουμε για να πάει η ροή από το Case 5 στο Case 100.(να εκτελέσει και τα δυο δηλαδή)

Module CheckIt {
      Pen 14 {
            select case 5
            case 5
            if true then {print "break" : Print "1000"} : Print "ok"
            case 100
            { Print 1000 : continue}
            case 500
                  Print "nothing" : Goto 100
            else case
                  Print "else case"
            end select
            Print "bad"
      }
      100 Print "ok"
}
Checkit


Τετάρτη 2 Ιανουαρίου 2019

Λατινικοί Αριθμοί

Ανέβηκαν δυο αναθεωρήσεις: Η αναθεώρηση 17 που διόρθωσε ένα λάθος, το οποίο διέγραφε το σωρό επιστροφής ρουτινών όταν χρησιμοποιούσαμε την φόρμα Έλεγχος. Η αναθεώρηση 18 που διόρθωσε ένα ζήτημα σε linux, με την φόρμα ελέγχου, όταν έμπαινε σε κατάσταση αναμονής "πάτωνε" το σύστημα λόγω μη σωστής απόκρισης σε ένα χαρακτηριστικό των Windows στο οποίο το wine δεν το δουλεύει σωστά (το wine είναι το πρόγραμμα που επιτρέπει να τρέχουν εφαρμογές των Windows σε linux). Δεν υπάρχουν ξεχωριστά αρχεία εκτέλεσης του διερμηνευτή, για windows και linux, ένα αρχείο m2000.exe καλεί ένα m2000.dll. Το m2000.dll είναι αντικείμενο που μπορεί να φορτωθεί από άλλες γλώσσες και να στέλνει κανείς πρόγραμμα και να το εκτελεί σε αυτό (το ίδιο κάνει το m2000.exe, όπου απλά το ξεκινάει, και η μόνη ευκολία είναι σε windows να εμφανίζει μια ετικέτα στο taskbar των windows)

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



Το πρόγραμμα Λατινικοί_Αριθμοί υπάρχει με αγγλικές εντολές, αλλά αποφάσισα να το γράψω με ελληνικές. Γίνεται εύκολα η μετατροπή στο διορθωτή (ενσωματωμένο στο διερμηνευτή της γλώσσας Μ2000), επιλέγουμε μια λέξη με απλό αριστερό κλικ σε κάποιο γράμμα της, και μετά πατάμε το F5. Ανοίγει μια φόρμα διαλόγου και ή κάνουμε άμεσα αντιγραφή από το πρόχειρο με ctrl+V ή γράφουμε αυτό που θέλουμε και με το ΟΚ αλλάζει παντού. Αυτή η αλλαγή έχει αναίρεση, πολλαπλή με Ctrl+Z (undo) και Ctrl+Y (redo).



Για να το τρέξουμε ξεκινάμε την Μ2000 με κλικ στο m2000.exe γράφουμε Σ Α και ανοίγει ο διορθωτής (Συγγραφή Α είναι κανονικά αλλά το Σ είναι συντόμευση - μπορούμε να έχουμε ταυτόχρονα τη Σ μεταβλητή, δεν έχει θέμα ο διερμηνευτής). Εκεί αντιγράφουμε το παρακάτω και με esc επιστρέφουμε στην γραμμή εντολών της Μ2000.
Γενικά μπορούμε να έχουμε πολλά τμήματα φορτωμένα, με μεγαλύτερα ονόματα από το Α, και έτσι δουλεύουμε αν θέλουμε να τρέχουμε δυο τρεις διαφορετικές εκδόσεις του ίδιου προγράμματος. Το τμήμα Α είναι εξ ορισμού γενικό, δηλαδή αν φτιάξουμε το Β με Σ Β τότε από το Β μπορούμε να καλέσουμε το Α εκτός και αν έχουμε τοπικό τμήμα Α το οποίο σκιάζει το γενικό. Η Μ2000 έχει δυο ειδών σκιάσεις, μια τοπική σκιάζει πάντα μια γενική με το ίδιο όνομα, και μια γενική με το ίδιο όνομα σκιάζει πάντα μια άλλη γενική με το ίδιο όνομα. Η διαφορά τους είναι ότι ένα τμήμα μέσα σε αυτό που επισκίασε την γενική έχει δικό του χώρο ονομάτων και έτσι αν η σκίαση στο πατρικό έγινε με τοπική τότε σε αυτό θα φαίνεται η υπάρχουσα γενική, αλλά αν έγινε με γενική τότε σε αυτό θα φαίνεται η νέα γενική. Η Μ2000 δηλαδή χρησιμοποιεί με τις σκιάσεις δυναμική συμπεριφορά ως προς τις μεταβλητές και τα τμήματα/συναρτήσεις.



Σημαντικά στοιχεία της Μ2000 είναι ο σωρός τιμών καθώς και το αντικείμενο εκτέλεσης. Σε κάθε εκτέλεση ο "δείκτης" προγράμματος δείχνει την εντολή σε ένα τμήμα/συνάρτηση/νήμα και αυτό δηλώνει το χώρο ονομάτων που ανήκει. Μπορεί ένα τμήμα να ανήκει σε ένα αντικείμενο (είναι μέθοδος). Βάσει του χώρου ονομάτων υπάρχει πρόσβαση στις τοπικές μεταβλητές. Ο "εκτελεστής" είναι συνάρτηση στον κώδικα του διερμηνευτή και συνδέεται με ένα αντικείμενο εκτέλεσης. Αυτό το αντικείμενο έχει διάφορα χρήσιμα στοιχεία μόνο για την συγκεκριμένη εκτέλεση. Ένα διαφορετικό νήμα έχει άλλο αντικείμενο εκτέλεσης ενώ τυγχάνει να έχει κοινό όνομα χώρου με άλλο νήμα. Κάθε τμήμα, συνάρτηση και νήμα εκτελείται σε δικό του αντικείμενο εκτέλεσης. Οι ρουτίνες δεν έχουν αντικείμενο εκτέλεσης, εκτελούνται από το τρέχον, και έχουν όνομα χώρου το όνομα του αντικείμενου εκτέλεσης. Υπάρχουν δυο ειδών ρουτίνες, οι απλές και αυτές με παρενθέσεις. Οι πρώτες δεν έχουν τοπικές μεταβλητές οι δεύτερες έχουν και αυτές για όσο τρέχει η ρουτίνα σκιάζουν τις τοπικές στο αντικείμενο εκτέλεσης. Στο πρόγραμμα παρακάτω χρησιμοποιώ μια ρουτίνα απλή για αρχικοποίηση, επειδή ότι φτιάχνει μένει (σε αντίθεση οι ρουτίνες με παρενθέσεις που φτιάχνονται με το Ρουτίνα/Τέλος Ρουτίνας, διαγράφουν ότι νέο αναγνωριστικό φτιάχνουμε).



Κάθε αντικείμενο εκτέλεσης εκτελεί κώδικα και εκτός από τιμές που βάζει και διαβάζει με αναγνωριστικά (μεταβλητές - πίνακες), έχει πρόσβαση στο σωρό τιμών. Η ιστορία με το σωρό τιμών ξεκίνησε με την FORTH, όταν την ανακάλυψα τη δεκαετία του 80. Ο σωρός εδώ δεν σχετίζεται με την έννοια του σωρού επιστροφής (return stack). Χρησιμοποιώ την λέξη σωρός ενώ και η λέξη Στοίβα θα ταίριαζε. Ο Σωρός λοιπόν είναι μια λίστα συνδεδεμένων τιμών, που μεγαλώνει όσο θέλουμε (όσο υπάρχει μνήμη), και μπορούμε να βάζουμε τιμές ή στη κορυφή ή στο πυθμένα. Επειδή ανασύρουμε τιμές από την κορυφή, όταν βάζουμε τιμές στη κορυφή ο σωρός λειτουργεί σαν LIFO (last in first out, ή το τελευταίο που μπαίνει βγαίνει πρώτο), ενώ όταν βάζουμε στο πυθμένα, ή τέλος, ο σωρός λειτουργεί ως FIFO (first in first out, ή το πρώτο που μπαίνει βγαίνει πρώτο). Η ανάσυρση τιμής λέγεται στα αγγλικά pop, ενώ το απλό διάβασμα peek. Στη Μ2000 δεν χρησιμοποιούμε αυτές τις λέξεις. Οι τιμές του σωρού μπορεί να είναι οτιδήποτε εκτός από τμήματα, δηλαδή μπορούμε ακόμα και συναρτήσεις να περάσουμε, και για τμήματα μπορούμε να περάσουμε αντικείμενα που έχουν τμήματα, δηλαδή έμμεσα περνάμε και τμήματα. Οι βασικές εντολές του σωρού είναι πέντε, η Διάβασε (ή Read) για να διαβάζουμε από τη κορυφή (ταυτόχρονα πετάει την τιμή, δηλαδή είναι το λεγόμενο pop), η Βάλε (ή Push) για να βάζουμε στη κορυφή, η Σειρά (ή Data) για να βάζουμε στο πυθμένα, η Άδειασε (ή Flush) για να διαγράφουμε όλες τις τιμές και η Πέτα για να πετάξουμε έναν αριθμό τιμών. Υπάρχουν εντολές που μετακινούν ένα ή περισσότερα στοιχεία από την κορυφή προς μια θέση και το ανάποδο (Φέρε και ΦέρεΠίσω, ή Shift και ShiftBack), καθώς και εντολή για αντιγραφή στοιχείων, ενός ή περισσότερων (Πάνω ή Over). Υπάρχουν συναρτήσεις (και μεταβλητές μόνο για ανάγνωση) που διαβάζουν από κορυφή και πετάνε το στοιχείο, αλλά και άλλες που διαβάζουν μόνο, από κορυφή ή άλλη θέση. Επίσης υπάρχουν συναρτήσεις που ελέγχουν τους τύπους των στοιχείων σε μια σειρά θέσεων, ή σε συγκεκριμένη.
Κάθε τμήμα δέχεται το σωρό τιμών αυτού που το κάλεσε, δηλαδή δεν έχει δικό του αποκλειστικό σωρό. Οι συναρτήσεις όταν κληθούν σε παράσταση καθώς και τα νήματα ξεκινούν με δικό τους σωρό τιμών. Το αναγνωριστικό [] κάνει κάτι πρωτότυπο, γυρνάει ένα δείκτη στο σωρό τιμών, και αφήνει ένα νέο αντικείμενο στο σωρό τιμών του τρέχοντος αντικείμενου εκτέλεσης (αυτό σημαίνει ότι αν έχουμε εκτέλεση σε τμήμα, θα αλλάξει και το αντικείμενο Σωρός του πατρικού). Με το δείκτη σε σωρό μπορούμε να κάνουμε εργασίες όπως στον τρέχοντα σωρό, με πρόσκαιρη σύνδεσή του ως τρέχοντα σωρό. Επίσης δεν υπάρχει δυνατότητα να κρατήσουμε σε δείκτη ένα σωρό αντικειμένου εκτέλεσης ταυτόχρονα με το αντικείμενο εκτέλεσης (αυτό για ασφάλεια). Δείτε στη συνάρτηση Τιμήλατινικού$() (το $ δηλώνει ότι επιστρέφει αλφαριθμητικό, αυτό είναι βασικό για τον διερμηνευτή), όπου με την Βάλε, γεμίζουμε τον τρέχον σωρό. Επιπλέον φτιάχνουμε ένα νέο αντικείμενο Σωρός με τιμές ανάμικτες, αλφαριθμητικές και αριθμητικές. Εδώ έχει σημασία η σειρά που διαβάζουμε. Η συνάρτηση πρώτα εκτελεί μια Διάβασε Λατ$, και μετά την Άδειασε, για να καθαρίσουμε το σωρό από τυχόν άλλες τιμές. Στη Μ2000 δεν γίνεται έλεγχος του αριθμού και του τύπου στο σημείο της κλήσης αλλά στο προορισμό της κλήσης, μέσω της Διάβασε. Δεν μπορούμε να διαβάσουμε αλφαριθμητικό αν υπάρχει αριθμητική τιμή. Επίσης αν θέλουμε λέμε στην Διάβασε τι τύπο θέλουμε να διαβάσουμε και αν δει κάτι μη συμβατό τότε γυρνάει σφάλμα. Αν και δεν βλέπουμε στη σύνταξη την εντολή Διάβασε Λατ$, ο διερμηνευτής την βάζει αυτόματα, επειδή η παρένθεση μετά το όνομα είναι μια συντακτική "ζάχαρη" που απλά μας δείχνει πιο ωραία στην εμφάνιση αλλά μένει "υπόγεια" στο πραγματικό τρόπο σύνταξης:
Συνάρτηση ΤιμήΛατινικού$ {
Διάβασε Λατ$
\\ άλλες εντολές
}
Ενώ στα τμήματα μπορούμε να επιστρέψουμε τιμές μέσω του σωρού τιμών, στις συναρτήσεις όταν καλούνται σε παραστάσεις (μπορούν να κληθούν με την Κάλεσε ως τμήματα), η επιστροφή τιμής γίνεται με το = (ή το -> αν γυρνούν δείκτη σε αντικείμενο Ομάδα - προχωρημένο θέμα). Η επιστροφή τιμής μπορεί να γίνει περισσότερες από μια φορές, και η συνάρτηση θα τερματίσει όταν εξαντλήσει το κώδικά της (μια εντολή Έξοδος τον εξαντλεί αμέσως). Εδώ λοιπόν βάζουμε το "άκυρο" ως αρχική τιμή επιστροφής.
Μετά εκτελούμε δυο δομές Σωρός { } όπου στο τέλος κάθε μπλοκ έχουμε την Κυκλικά. Αυτή η εντολή κάνει το μπλοκ να εκτελεστεί ξανά, θέτοντας μια σημαία για επανάληψη. Η σημαία ελέγχεται όταν εξαντληθεί ο κώδικας του μπλοκ. Μια εντολή έξοδος έχει προτεραιότητα σε σχέση με την σημαία επανάληψης. Η εντολή Κυκλικά (loop) πρέπει να εκτελεστεί κάθε φορά για να κάνει το μπλοκ να έχει επανάληψη, επειδή η σημαία είναι μιας χρήσεως και μετά γίνεται Ψευδής (χωρίς επανάληψη). Η εντολή Διέκοψε είναι η "βαριά" έξοδος, γιατί όχι μόνο κάνει έξοδο από το μπλοκ αλλά συνεχίζει και πιο πάνω, εδώ κάνει έξοδο και από την συνάρτηση (και για το λόγο αυτό έχουμε δώσει τιμή επιστροφής- αν δεν είχαμε δώσει μια τιμή η συνάρτηση θα γύρναγε την εξορισμού τιμή για αλφαριθμητικές συναρτήσεις, το κενό αλφαριθμητικό). Έτσι στη συνάρτηση η Διέκοψε σημαίνει ότι έχουμε άκυρη τιμή στο Λατ$, δεν υπάρχει αριθμός για επιστροφή (ο αριθμός γυρνάει ως αλφαριθμητικό).
Πριν εκτελέσουμε άλλες εντολές το μπλοκ στη Σωρός κοιτάμε αν αυτός είναι άδειος με την Κενό, μια μεταβλητή μόνο για ανάγνωση που σχετίζεται με τον τρέχον σωρό. Αν έχουμε κενό τότε κάνουμε έξοδο (σημαίνει ότι δεν υπάρχουν άλλες τιμές για επεξεργασία). Αλλιώς συνεχίζουμε διαβάζοντας τρεις τιμές κάθε φορά. Μας ενδιαφέρει ένα αλφαριθμητικό που μας λέει ποιοι χαρακτήρες δεν πρέπει να υπάρχουν μετά, μετά μια αριθμητική τιμή και τέλος το διπλό χαρακτήρα που θα αλλάξουμε - στο πρώτο μπλοκ-. Για κάθε διπλό χαρακτήρα κάνουμε μια αναζήτηση θέσης με την Θέση(). Αν υπάρχει αυτός ο διπλός χαρακτήρας τότε η θέση του θα είναι μεγαλύτερη από μηδέν. Σε ένα αλφαριθμητικό προσωρινή_τιμή$ (αν και αυτή η μεταβλητή δεν είναι προσωρινή, γιατί θα μείνει μετά την έξοδο από το μπλοκ, και απλά εδώ την δημιουργούμε με εκχώρηση τιμής) βάζουμε το υπόλοιπο μετά τη θέση όπου βρήκαμε το διπλό χαρακτήρα για να κάνουμε έλεγχο.



Στη συνέχεια επειδή θέλουμε να δούμε αν υπάρχουν κάποιοι μη επιτρεπτοί χαρακτήρες, τους αφαιρούμε με τη Φίλτρο$() (δεν θα γίνει τίποτα αν δεν υπάρχουν), και μετά κοιτάμε το τελικό μήκος. Αν δεν έχει αλλάξει είμαστε εντάξει αλλιώς πάμε για Διέκοψε (Break).
Η ουσιαστική εντολή εδώ είναι η Παρεμβολή, ένας τρόπος να αλλάξουμε χαρακτήρες σε ένα αλφαριθμητικό χωρίς να βγάλουμε αντίγραφο.(διαφορετικά θα έπρεπε να πάρουμε δυο μέρη του αλφαριθμητικού και να συνθέταμε το πρώτο, αυτό που θέλουμε να παρεμβληθεί, και το τελευταίο, σε ένα αλφαριθμητικό, και το αποτέλεσμα να το γράφαμε στο τρέχον αλφαριθμητικό). Η ιδέα εδώ είναι να προετοιμάσουμε το αλφαριθμητικό Λατ$, με "μονάδες", ώστε να αθροιστούν σωστά. Εδώ η Παρεμβολή αφαιρεί χαρακτήρες. Για κάθε τιμή που αφαιρούμε ως χαρακτήρες αυξάνουμε το άθροισμα.

Την τελική άθροιση την κάνει το τελευταίο μέρος, όπου ένα αντικείμενο σωρός μας λέει τι πρέπει να κάνουμε. Δείτε εδώ ότι έχει σημασία ότι κάθε φορά ψάχνουμε από το μεγαλύτερης αξίας, και δεν το αναζητούμε ξανά σε άλλες θέσεις εκτός από τις πρώτες. Αν στο τέλος το Λατ$ δεν μείνει κενό αλφαριθμητικό τότε έχουμε άκυρο νούμερο! Εδώ κάθε χαρακτήρας το πολύ να έχει τρεις εγγραφές. Έχει γίνει ξεκαθάρισμα από πριν για εκείνους τους χαρακτήρες που δεν επιτρέπονται μετά από ζευγάρια χαρακτήρων.







Τμήμα Λατινικοί_Αριθμοί {
      Άδειασε
      Διαμέσου Αρχικοποίηση
      Έγγραφο Εγγ$
      Ενώ όχι κενό {
            Διάβασε λατ$
            Τύπωσε λατ$;"=";ΤιμήΛατινικού$(λατ$)
            Εγγ$=λατ$+"="+ΤιμήΛατινικού$(λατ$)+{
            }
      }
      Πρόχειρο Εγγ$
      Τέλος
Αρχικοποίηση:
      Συνάρτηση ΤιμήΛατινικού$(λατ$) {
            Άδειασε
            Βάλε "CM", 900,"MD","CD", 400,"MD", "XC", 90,"DL", "XL", 40,"X", "IX",9,"V", "IV",4,"I"
            διπλό_λατινικό=[]
            απλό_λατινικό=Σωρός:="M", 1000, "D", 500, "C",100, "L",50,"X", 10, "V", 5, "I",1
            ="άκυρο"
            σύνολο=0
            μια_τιμή=0
            μετρητής=0
            Σωρός διπλό_λατινικό {
                  Αν κενό Τότε Έξοδος
                  Διάβασε μη_έγγυροι_χαρακτήρες$, μια_τιμή, χαρακτ$
                  i=Θέση(λατ$,χαρακτ$)
                  Αν i >0 Τότε {
                        προσωρινή_τιμή$=Μεσ$(λατ$,i+2)
                        L=Μήκος(προσωρινή_τιμή$)
                        Αν L>0 Τότε Αν Μήκος(Φίλτρο$(προσωρινή_τιμή$, μη_έγγυροι_χαρακτήρες$))<>L Τότε λατ$="A": Διέκοψε
                        Αν Θέση(λατ$,Μεσ$(λατ$,i,1))<i Τότε λατ$="A": Διέκοψε
                        Παρεμβολή i, 2 λατ$="" : σύνολο+=μια_τιμή
                  }
                  Κυκλικά
            }
            Σωρός απλό_λατινικό {
                  Αν κενό Τότε Έξοδος
                  Διάβασε χαρακτ$, μια_τιμή
                  μετρητής=0
                  Ενώ Αρισ$(λατ$,1)=χαρακτ$ {
                         Παρεμβολή 1, 1 λατ$=""
                         μετρητής++
                         σύνολο+=μια_τιμή
                  }
                  Αν μετρητής>3 Τότε Διέκοψε
                  Κυκλικά
            }
            Αν Μήκος(λατ$)>0 Τότε Έξοδος
            =Str$(σύνολο,1033)
      }
      Σειρά "MMMCMXCIX", "LXXIIX", "MMXVII", "LXXIX", "CXCIX","MCMXCIX","MMMDCCCLXXXVIII"
      Σειρά "CMXI","M","MCDXLIV","CCCC","IXV", "XLIXL","LXXIIX","XXXIX", "XXXX", "XIXX","IVI", "XLIX"
      Επιστροφή
}
Λατινικοί_Αριθμοί


Εμφάνιση στην οθόνη (και συνάμα αντιγράφει και στο πρόχειρο):

MMMCMXCIX=3999
LXXIIX=άκυρο
MMXVII=2017
LXXIX=79
CXCIX=199
MCMXCIX=1999
MMMDCCCLXXXVIII=3888
CMXI=911
M=1000
MCDXLIV=1444
CCCC=άκυρο
IXV=άκυρο
XLIXL=99
LXXIIX=άκυρο
XXXIX=39
XXXX=άκυρο
XIXX=29
IVI=άκυρο
XLIX=49



Ανώτερο από το παραπάνω, το XLIXL πρέπει να βγαίνει άκυρο, όπως και το ΧΙΧΧ και το IVM.

Τμήμα Λατινικοί_Αριθμοί {
      Άδειασε
      Διαμέσου Αρχικοποίηση
      Έγγραφο Εγγ$
      Ενώ όχι κενό
            Διάβασε λατ$
            Τύπωσε λατ$;"=";ΤιμήΛατινικού$(λατ$)
            Εγγ$=λατ$+"="+ΤιμήΛατινικού$(λατ$)+{
            }
      Τέλος Ενώ
      Πρόχειρο Εγγ$
      Τέλος
Αρχικοποίηση:
      Συνάρτηση ΤιμήΛατινικού$(λατ$) {
            Άδειασε
             ="άκυρο"           
            Αν Φίλτρο$(λατ$,"MDCLXVI")<>"" Τότε Έξοδος
            \\ θα μπουν ανάποδα, το τελευταίο πρώτο.
            Βάλε "CM", "MD", "Q","CD", "MD","W", "XC", "DL", "E","XL", "X","R", "IX","V","T", "IV","I","Y"
            διπλό_λατινικό=[]
            \\ θα μπουν με το τελευταίο να είναι τελευταίο.
            Σειρά "M", 1000, "Q",900,"D", 500,"W", 400,"C",100,"E",90, "L",50,"R", 40,"X", 10, "T", 9, "V", 5, "Y", 4, "I",1
            απλό_λατινικό=[]
            σύνολο=0
            μια_τιμή=0
            μετρητής=0
            Σωρός διπλό_λατινικό {
                  Αν κενό Τότε Έξοδος
                  Διάβασε απ$, μη_έγγυροι_χαρακτήρες$,χαρακτ$
                  ι=Θέση(λατ$,χαρακτ$)
                  Αν ι >0 Τότε
                        προσωρινή_τιμή$=Μεσ$(λατ$,ι+2)
                        λ=Μήκος(προσωρινή_τιμή$)
                        Αν λ>0 Τότε Αν Μήκος(Φίλτρο$(προσωρινή_τιμή$, μη_έγγυροι_χαρακτήρες$))<>λ Τότε λατ$="A": Έξοδος
                        Αν Θέση(λατ$,Μεσ$(λατ$,ι,1))<ι Τότε λατ$="A": Έξοδος
                        insert ι, 2 λατ$=απ$ '  2 λέμε αλλα δίνουμε 1 χαρακτήρα και ο μεταφραστής θα βάλει ένα διάστημα
                  Τέλος Αν
                  Κυκλικά
            }
            λατ$=Φίλτρο$(λατ$," ")
            Σωρός απλό_λατινικό {
                  Αν κενό Τότε Έξοδος
                  Διάβασε χαρακτ$, μια_τιμή
                  μετρητής=0
                  Ενώ Αρισ$(λατ$,1)=χαρακτ$
                         Παρεμβολή 1, 1 λατ$=""
                         μετρητής++
                         σύνολο+=μια_τιμή
                  Τέλος Ενώ
                  Αν μετρητής>3 Τότε έξοδος
                  Κυκλικά
            }
            Αν Μήκος(λατ$)>0 ή μετρητής>3 Αλλιώς
            =Γραφή$(σύνολο,1033)
            Τέλος Αν
      }
      Σειρά "MMMCMXCIX", "LXXIIX", "MMXVII", "LXXIX", "CXCIX","MCMXCIX","MMMDCCCLXXXVIII"
      Σειρά "CMXI","M","MCDXLIV","CCCC","IXV", "XLIXL","LXXIIX","IVM"
      Σειρά "XXXIX", "XXXX", "XIXX","IVI", "XLIX","XCIX","XCIV","XLVIII"
      Επιστροφή
}
Λατινικοί_Αριθμοί


Revision 18 Version 9.6

Revision 18 is like Revision 17 except for Wine environment (linux). Now when m2000.exe run in linux (because always check at the beginning if it runs in wine, we have a flag inside code to indicate that when we need it), change strategy and use DoEvents and not wait for  multiple object through the use of  MsgWaitForMultipleObjects ( a user32 function). Perhaps there is a better solution, but for now this works fine. (there is also a IsWine read only variable to get the flag in M2000 code).

Αναθεώρηση 16 Έκδοση 9.6

Αναθεώρηση 16
Αναβάθμιση του διερμηνευτή σε αρκετά πράγματα στη γλώσσα. Καλύτερος Έλεγχος με την φόρμα ελέγχου. Η φόρμα ανοίγει με την εντολή Δοκιμή, ως έχει ή με όνομα τμήματος για δοκιμή. Μπορούμε να την συμπεριλάβουμε στο κώδικα και να δώσουμε Δοκιμή  "όνομα σημείου διακοπής", και έτσι θα ανοίξει στο σημείο που θέλουμε και θα μας δείξει το κώδικα. Η φόρμα έλεγχος έχει τρία πεδία ως επίπεδα πλήκτρα δεξιά για εκτέλεση ανά εντολή  (δουλεύει και όταν έχουμε νήματα, τα οποία περιμένουν για να εκτελεστούν με το πάτημα του πλήκτρου αυτού), για αργή εκτέλεση (μάλλον έχει γίνει και αυτή γρήγορη), καθώς επίσης και σταμάτημα την εκτέλεσης. Αριστερά έχει τρεις ετικέτες οι οποίες λειτουργούν και ως πλήκτρα. Η πρώτη δείχνει τι τρέχει, πχ μια συνάρτηση, ένα τμήμα, μια λάμδα συνάρτηση ή ένα νήμα (δείχνει τον αριθμό νήματος). Η δεύτερη δείχνει το όνομα εντολής ή του τμήματος που εκτελούμε ή της μεταβλητής που αλλάζουμε ή εκχωρούμε τιμή. Η τρίτη δείχνει τη συνέχεια της προηγούμενης. Στη πρώτη με κλικ ανοίγει μια φόρμα που δείχνει το κώδικα αυτού που εκτελούμε. Στη δεύτερη αν είναι εντολή δείχνει την βοήθεια, το τι κάνει δηλαδή. Στη τρίτη εναλλάσσουμε τη προβολή του μεσαίου μεγάλου πεδίου. Αυτό το πεδίο δείχνει ή το σωρό τιμών  καθώς και τιμές που θέλουμε να δούμε (και τις βάζουμε στο κάτω πεδίο) ή κώδικα χρωματισμένο με σημειωμένη την εντολή που εκτελείται. Το κάτω πεδίο, έχει δυο λειτουργίες, και αλλάζουν με το να διαγράψουμε το περιεχόμενο και τότε εμφανίζεται η αλλαγή στο σημάδι εισαγωγής (prompt) όπου ως ? σημαίνει να τυπώνουμε μια σειρά παραστάσεων στο μεσαίο μεγάλο πεδίο και με το > μπορούμε να δίνουμε εντολές άμεσα, ακόμα και για αλλαγή κώδικα, αλλά η αλλαγή θα φανεί όταν ξεκινήσει ξανά ένα τμήμα ή όταν "τραβήξει" από αυτό κάποια ρουτίνα. Η φόρμα έλεγχος μεγεθύνεται, όχι για να χωρέσει περισσότερα, αλλά για να μεγαλώσουν τα γράμματα (δείχνει πάντα τα ίδια σε ότι μέγεθος και να την έχουμε). Η αλλαγή γίνεται από την κάτω δεξιά γωνία, όπως μεγαλώνουμε τις φόρμες (αλλά εδώ έχουμε μεγέθυνση γραμματοσειράς και πεδίων). Ενώ με τις φόρμες χρήστη μπορούμε να έχουμε "κυρίαρχα" (modal) παράθυρα (σαν σωρός το ένα πάνω στο άλλο, κλειδώνοντας τα από κάτω), η φόρμα Έλεγχος δεν κλειδώνει, και μένει μαζί με ότι φόρμες έχουμε δημιουργήσει. Οι φόρμες δουλεύουν από XP έως Windows 10 και σε Linux, και εμφανίζονται σε όλα τα λειτουργικά ίδιες. Ο κώδικάς τους συμπεριλαμβάνεται στο διερμηνευτή, δηλαδή δεν αποτελούν χωριστή βιβλιοθήκη, αλλά ενσωματωμένη.

Αναθεώρηση 15
Πρώτη αναθεώρηση για το 2019!
Ουσιαστικά αφαιρέθηκαν δυο λάθη, και βελτιώθηκε ο διερμηνευτής.
Η βελτίωση έγινε στην Επίλεξε Με και στα Νήματα όταν τρέχουν με ΣΧΕΔΙΟ.ΝΗΜΑΤΩΝ ΤΑΥΤΟΧΡΟΝΟ. Σε αυτό το σχέδιο κάθε νήμα τρέχει μια εντολή και δίνει τον έλεγχο στον ενσωματωμένο στον διερμηνευτή ελεγκτή νημάτων για να επιλέξει το νήμα που θα τρέξει.  Στη βελτίωση έγινε το εξής, η εκτέλεση γίνεται ανά γραμμή κώδικα και όχι ανά εντολή. Παρέμεινε ως είχε, ότι ένα μπλοκ κώδικα θα εκτελεστεί αυτούσιο. Στην Επίλεξε Με κάθε Με μπορεί να έχει μια γραμμή κώδικα ή ένα μπλοκ. Παλαιότερα αν η γραμμή κώδικα είχε άνω κάτω τελεία με εντολή μετά ο διερμηνευτής την άφηνε γιατί "καθάριζε" την υπόλοιπη γραμμή. Τώρα ο διερμηνευτής θα εκτελέσει όλες τις εντολές μέχρι να φύγει όλη η γραμμή.
Τα λάθη ήταν: Ένα στην Κάνε (def) όπου αν δίναμε λογική μεταβλητή και ζητάγαμε το Αληθές τότε δεν το έπαιρνε (παλαιότερα το έκανε, αλλά λόγω αλλαγής κώδικα...το έχασε). Το άλλο είχε να κάνει με την Αν σε μια περίπτωση που σχετίζεται με τη βελτίωση παραπάνω, δηλαδή όταν ζητάμε από το διερμηνευτή να εκτελέσει μια εντολή. Πρέπει η Αν να είναι σε μια γραμμή, αφού τελικά θα εκτελεστεί μια γραμμή. Όμως τώρα μπορούμε να έχουμε όλα τα μέρη μιας Αν, δηλαδή το Αλλιώς.Αν όσες φορές θέλουμε και το Αλλιώς.