Σάββατο, 14 Σεπτεμβρίου 2019

Τελική Αναθεώρηση 40 για την έκδοση 9.8

Ολοκληρώθηκε η 9.8 έκδοση της Μ2000 με την αναθεώρηση 40. Ο διερμηνευτής έγινε πιο γρήγορος. Τρέχει από Windows XP και πάνω, και σε Linux με Wine. Επίσης τρέχει σε συστήματα με πολλαπλές οθόνες.

Παρασκευή, 13 Σεπτεμβρίου 2019

Revision 39 Version 9.8

New revision and now we can use surrogates for UTF16LE, which is the encoding system for M2000 strings. M2000 is an interpreter written and compiled in Visual Basic 6.



M2000 Environment is built with VB6 naked forms (without title and control buttons). The problem of control buttons in VB6 exist in exit button [X}. When we press this button any thread halted until button released,or perform the hit. Because M2000 has a thread internal system, which implements an interval per thread, this halt is not allowed. So from version 7 of M2000 interpreter introduced a GUI system based in a single type form, and with a basic user control the glis4.ctl .This gui control in association with other objects, which can handle events make all the GUI controls of M2000. Before the GUI system was a form to emulate a console with graphics, like the screen of an 8-bit computer, with scolling, and split screen function (the low part of console can scroll, and the top part is static). This form actually exist, and is the main form when we open the interpreter in immediate mode. The editor for M2000 works with a TextViewer.cls (an object in VB6) similar to EditBox, the editor for gui. All gui elements have siblings for some utility forms, with a basic difference: The gui controls send events to interpreted user program at execution time, the other controls send the events to compiled code inside the interpreter.



So the job to include the surrogates for M2000 had three parts:

1. The console form which commands like INPUT and PRINT (similar to BASIC) use an internal key buffer (we can wait for key using KEY$ without stopping any thread - $ used when a string to indicate a string type of identifier). Characters on console layers (there are 32 plus 2 more layers)  and on user forms layers. can be displayed in a canvas style, using the Print command, where each character is at specific row and column, or using proportional spacing  using columns, justification, at specific rows, or by using the Report command we use proportional spacing but without columns, using tabs characters if we wish, for rendering text at a specific with, where only the left margin adjusted to the cursor row position. A last option is the Legend command which we can print text at graphic positions, using angle and justification.



From these four types of printing text in forms (same for printer paper), the last one (Legend command) use Win32 calls to perform all the tasks. The Report command has own justification routine (written by me), and before this revision only utf-16 diacritics handled proper. The same for the Print command



2. Editors updated to include the surrogates. The cursor always move one byte, but when discover a surrogate advance one more, or do the opposite if the cursor move to the left. Del and backspace/

3. Functions Chrcode$(numeric_expression) and Chtcode(string_expression) now work with surrogates. chrcode(chrcode$(0x10437))=0x10437.


This is an image of five programs running, one with full screen, another with modification to console, using transparency (a clock gadget), plus three editors using M2000 user forms. One editor for M2000 code, one for C# and another for an educational tiny language called ΓΛΩΣΣΑ (means language). From all editors we can copy to clipboard each time to two forms, one for plain text (utf-16) and one for html (utf-8).



Δευτέρα, 9 Σεπτεμβρίου 2019

Αναθεώρηση 36 Έκδοση 9.8

Σε αυτήν την αναθεώρηση έφτιαξα το EditBox (στοιχείο ελέγχου σε φόρμες) και το υποκείμενο Document, ώστε να μπορούν να χειριστούν κώδικα της C#. Μάλιστα επειδή υπάρχει ο μεταφραστής της C# σε κάθε υπολογιστή με Windows και .Net, μπορούμε να μεταφράσουμε το πρόγραμμα και να εκτελέσουμε το αποτέλεσμα. Το ενδιαφέρον για μένα προγραμματιστικά για τον χρωματισμό της C# ήταν να μπορέσω με κατάλληλες αλλαγές να κάνω το EditBox να λειτουργεί πρώτα για λέξεις όπου τα πεζά-κεφαλαία να έχουν σημασία (ενώ στη Μ2000 όπως και στην Basic δεν είναι σημαντικά. Έτσι όταν επιλέγουμε ένα αναγνωριστικό με τα F2 και F3 πάμε στο ίδιο πάνω ή κάτω (αν υπάρχει), με τα γράμματα ως έχουν, έτσι το Book με το book είναι διαφορετικά, και αν αναζητούμε το Book δεν θα μας δώσει το book. Επίσης με το F5 αλλάζουμε μια λέξη και τώρα η αλλαγή γίνεται μόνο για τη λέξη με τα συγκεκριμένα και κεφαλαία.

Μια άλλη αλλαγή έγινε για να μπορώ να βλέπω τις λέξεις χωρίς τις τελείες, έτσι το Book.Title είναι δυο λέξεις, και αν κάνω αλλαγή στο Book με το F5 ως One τότε θα αλλάξει οπουδήποτε ακόμα και στο Book.Title (στη Μ2000 δεν γίνεται αυτό, γιατί το Book.Title τότε το βλέπει σαν μια λέξη). Αν επιλέξω με shift και βελάκια ή με shift και το ποντίκι, όλη τη λέξη τότε το F5 θα μας ζητήσει μια λέξη για να αλλάξει αυτό που δώσαμε με κάτι άλλο. Έτσι μπορούμε να αλλάξουμε και συνδεδεμένες λέξεις με τελεία. Επιπλέον μια άλλη αλλαγή έγινε με τις παρενθέσεις. Ενώ στη Μ2000 χρωματίζονται με τρεις διαφορετικούς τρόπους, ανάλογα αν είναι σε συνάρτηση εσωτερική ή σε συνάρτηση χρήστη ή είναι απλά παρενθέσεις, στο χρωματισμό για την C# έκανα τις παρενθέσεις ένα χρώμα. Τις συναρτήσεις τις έχουμε με ξεχωριστό χρώμα στις παρενθέσεις. Επίσης τρεις άλλες  αλλαγές χρειάζονταν, μια για το πώς διευθετούμε τα [ ], μια άλλη για τα < > και μια άλλη για τα σύμβολα στην αρχή της γραμμής (μπορούμε να έχουμε μια γραμμή εντολών σπασμένη σε δυο ή περισσότερες γραμμές).

Ο διορθωτής μπορεί να γυρίσει σε txt από cs, και το ανάποδο.




 Αν ένα εκτελέσιμο τρέχει μόνο σε κονσόλα και εξάγει στοιχεία μπορούμε να το εκτελέσουμε με εξαγωγή στο out.txt και να το ανοίξουμε με την τελευταία επιλογή στο RUN.


Αν ένα  πρόγραμμα δέχεται εισαγωγή από κονσόλα το τρέχουμε χωρίς την επιλογή για έξοδο στο out.txt
Αν στο πρόγραμμά μας υπάρχει το System.Windows.Forms, τότε  κατά την εκτέλεση της επιλογής Compile εφαρμόζει αυτόματα το target:winexe και έτσι δεν χρησιμοποιεί το out.txt και στην εκτέλεση τρέχει την παραθυρική εφαρμογή. Στο info όταν ανοίξουμε το cs θα εμφανίσει το pendulum.cs και μπορούμε να το κάνουμε compile, και όταν γίνει θα ενημερωθεί το μενού και θα μας επιτρέψει να το τρέξουμε. 

Πέμπτη, 5 Σεπτεμβρίου 2019

Αναθεώρηση 34, Έκδοση 9.8

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

Οι συναρτήσεις που τώρα εμφανίζονται στη βοήθεια είναι οι παρακάτω:
#ΑΘΡ(), #ΑΝΑΠ(), #ΑΝΤ(), #ΘΕΣΗ(), #ΜΕΓ(), #ΜΕΡΟΣ(), #ΜΙΚ(), #ΠΑΚ(), #ΤΙΜΗ()
#ΜΕΓ$(), #ΜΙΚ$(), #ΠΑΚ$(), #ΤΙΜΗ$()

Αυτές είναι ειδικές συναρτήσεις γιατί πρέπει να κληθούν με ένα αποτέλεσμα tuple στα αριστερά τους:

\\ #αθρ() ή #sum()
Τύπωσε (1,2,3)#αθρ()=6
Πίνακας Α(10)=1
Τύπωσε Α()#αθρ()=10
μ=(1,2,3,4)
κ=(1,2,3)
Τύπωσε μ#αθρ()=10
\\ δεν χρειάζονται παρενθέσεις αν έχουμε συνθήκη
Τύπωσε (1,2,3,4)#αθρ()>(1,2,3)#αθρ()
\\ χρειάζονται παρενθέσεις εδώ
Τύπωσε ((1,2,3,4)#αθρ())+((1,2,3,4)#αθρ())=20
\\ δεν χρειάζονται παρενθέσεις αν έχουμε μεταβλητή που δείχνει σε πίνακα
Τύπωσε μ#αθρ()>κ#αθρ()
Τύπωσε μ#αθρ()+μ#αθρ()=20
\\ #αναπ() ή #rev()
Τύπωσε (1,2,3)#αναπ()  ' δίνει 3 2 1
\\ #μέρος() ή #slice()
Τύπωσε μ     ' 1 2 3 4 
Τύπωσε μ#μέρος(1,2) ' 2,3
\\ #μεγ() ή #max() \\ #μεγ$() ή #max$() για αλφαριθμητικά αλ=("α","β","γ") Τύπωσε αλ#μεγ$()="γ" Τύπωσε μ#μέρος(1,2)#μεγ()=3 \\ #μικ() ή #min() \\ #μικ$() ή #min$() για αλφαριθμητικά Τύπωσε αλ#μικ$()="α" Τύπωσε μ#μέρος(1,2)#μικ()=2

Τύπωσε μ#μέρος(1,2)#αθρ()=5
\\ #τιμη() ή #val() \\ #τιμη$() ή #val$() για αλφαριθμητικά
Τύπωσε μ#τιμή(2)=3
μ=((1,2,3),(4,5,6)) Τύπωσε μ#τιμή(1)#αθρ()=15 Τύπωσε μ#τιμή(0)#αθρ()=6
Δες οκ { Τύπωσε μ#τιμή(2)=3 } Αν λάθος ή όχι οκ Τότε Τύπωσε Λάθος$ ' Δείκτης εκτός ορίων
\\ #θέση() ή #pos() μ=(1,1,0,1,1,1,1,0,1,1,0) Τύπωσε μ#θέση(1,0,1)=1 ' 2η θέση (1+1) Τύπωσε μ#θέση(5 -> 1,0,1)=6 ' 7η θέση Τύπωσε μ#θέση(2,3)=-1 ' δεν υπάρχει μ=(1,"α","β","γ",2,"γ","δ","ε") Τύπωσε μ#αθρ()=3 ν=μ#θέση("γ","δ") Αν ν>=0 Τότε Τύπωσε μ#μέρος(ν-1,ν+2) ' 2 γ δ ε

\\ #φίλτρο() ή #filter() \\ δουλεύει με μια λάμδα συνάρτηση α=(1,2,3,4,5,6,7,8,9)
μονός=λάμδα (χ)->χ υπόλοιπο 2=1 β=α#φίλτρο(μονός, (,)) Τύπωσε α#φίλτρο(μονός, (,)) ' 1,3,5,7,9 Τύπωσε β ' 1,3,5,7,9
\\ #αντ() ή #map() αντ1=λάμδα ->{ Βάλε ΧαρΚωδ$(αριθμός+64) } ζ=α#φίλτρο(μονός)#αντ(αντ1) Τύπωσε ζ ' A C E G I
\\ #πακ() ή #fold() \\ #πακ$() ή #fold$() για αλφαριθμητικό \\ δουλεύει με μια λάμδα συνάρτηση πακ2=λάμδα ->{ φερε 2 βάλε γραμμα$+"-"+γραμμα$ } Τύπωσε "["+α#φίλτρο(μονός)#αντ(αντ1)#πακ$(πακ2, "CODE")+"]"="[CODE-A-C-E-G-I]" συνάρτηση σειρά_γραμμάτων (αρχικό$, πόσα, φορές) { α=(,) β=χαρκωδ(αρχικό$)-1 για ι=1 εως πόσα προσθήκη α, (επαν$(χαρκωδ$(β+ι), φορές),) επόμενο =α }
\\ κλείσμο της ζ αντ1=λάμδα ζ=σειρά_γραμμάτων("D", 10, 2) ->{ Βάλε ζ#τιμή$((αριθμός-1) υπολ 10) }
Τύπωσε "["+α#φίλτρο(μονός)#αντ(αντ1)#πακ$(πακ2, "CODE")+"]"="[CODE-DD-FF-HH-JJ-LL]"

\\ φτιάχνουμε ένα γεγονός που είναι μια λίστα συναρτήσεων \\ μπορούμε να δώσουμε συνάρτηση από υπάρχον αντικείμενο τύπου ομάδα \\ με αυτόν τον τρόπο μαζεύουμε τα αποτελέσματα στο έγγραφο κωδικοί$ \\ η ομάδα έχει δυο ονόματα, το αποτέλεσμα και το αποτέλεσμα$ \\ το πρώτο το χρησιμοποιούμε για τις ιδιότητες, το δεύτερο για το αποτέλεσμα \\ επειδή εδώ είναι αλφαριθμητικό. Γεγονός Αλφα { Διάβασε Κ, &Τ$ } Ομάδα αποτέλεσμα$ { Ιδιωτικό: Ζ=("1ΑΒ","2ΓΥ","ΚΜΝ","832","5ΝΜ","ΚΖΛ","Μ13","ΤΚΛ","1Α5","234") Λ=(5,8,1,2,0,3,4,7,9,6) Μ=0 Τελική Τ$={#Τέλος Κωδικών } Έγγραφο Κωδικοί$={#Κωδικοί: } Δημόσιο: Αξία { =.Κωδικοί$+.Τ$ } Συνάρτηση Τελική κοίτα (που ως ακεραιος, &α$) { που=απολ(που) υπολ 10 α$=#τιμη$(#τιμη(που)) .Κωδικοί$<=Αν$(=0 ->"","-")+α$ ++ Αν =5 Τότε .Κωδικοί$<={ } <=0 Τέλος ΑΝ } }
\\ οι συναρτήσεις που δίνουμε πρέπει να έχουν την ίδια υπογραφή (αυτό θα φανεί στην εκτέλεση) Γεγονος Αλφα Νέο &αποτέλεσμα.κοίτα() αντ1=λάμδα Αλφα, αυτό$="" ->{ Κάλεσε Γεγονός Αλφα, αριθμός-1, &αυτό$ Βάλε αυτό$ } Τύπωσε "["+α#φίλτρο(μονός)#αντ(αντ1)#πακ$(πακ2, "CODE")+"]"="[CODE-ΚΖΛ-2ΓΥ-1ΑΒ-5ΝΜ-234]"
\\ τυπώνει με αναλογική γραφή κείμενο. Περιμένει στα 3/4 τη σελίδας για να προχωρήσει \\ να δώσουμε διάστημα ή πάτημα αριστερού πλήκτρου ποντικιού Αναφορά αποτέλεσμα$ Τύπωσε "["+α#μέρος(4,8)#αντ(αντ1)#πακ$(πακ2, "CODE")+"]"="[CODE-1ΑΒ-832-5ΝΜ-ΤΚΛ-234]" \\ τυπώνει στην κονσόλα σαν έξοδος σε αρχείο με το κανάλι -2 με μη αναλογική γραφή Τύπωσε #-2, αποτέλεσμα$ Σημ { #Κωδικοί: ΚΖΛ-2ΓΥ-1ΑΒ-5ΝΜ-234 1ΑΒ-832-5ΝΜ-ΤΚΛ-234 #Τέλος Κωδικών }


Τρίτη, 3 Σεπτεμβρίου 2019

Αναθεώρηση 33 Έκδοση 9.8

Έφτιαξα την execute για βάσεις δεδομένων να εκτελεί πολλαπλές εντολές, με διαχωριστικό το ελληνικό ερωτηματικό. Δεν είχα ασχοληθεί πολύ με το θέμα, και είχα αφήσει την execute να μην επιστρέφει τιμές. Ίσως γιατί υπήρχαν άλλες εντολές για ανάκτηση πεδίων από αρχείο από βάση δεδομένων (εδώ λέμε αρχείο το πίνακα σε μια βάση). Όμως το αποφάσισα καθώς διάβαζα για την SQL και χθες το έφτιαξα.

Επειδή χρησιμοποιώ το αντικείμενο ADOdb και με εξ ορισμού τύπου την βάση mdb της Access, η SQL είναι λίγο διαφορετική. Κυρίως στο πώς ορίζουμε πεδία σε πίνακες.

Στο παράδειγμα παρακάτω με ενδιαφέρει να δείξω τη χρήση του  NULL. Μπορούμε σε ένα πεδίο να έχουμε NULL τιμή. Αυτό δεν σημαίνει μηδενικού μήκους αλφαριθμητικό (κενό). Με την χρήση της εντολής ΑΝΑΚΤΗΣΗ (RETRIEVE) δεν έχουμε επιστροφή NULL, μετατρέπεται σε κενό αλφαριθμητικό.


Το παράδειγμα υπάρχει στο νέο info.gsb ως baseG

Need new revision 33, version 9.8

\\ remove base if exist in current directory
base "gSQL"
\\ Make a new table and insert values (M2000 use ADOdb with a special connection string)
\\ data taken from example's of MOSH HAMEDANI for SQL, see this video
\\ https://www.youtube.com/watch?v=7S_tz1z_5bA
\\ Create table changed from original to be used here, but the schema is the same.
\\ Maybe integer definition maybe not the same with original int(11).
\\ We use 3 times execute. One return nothing, but the other two return a record set each \\ recordset saved to stack of values (as top value each time) \\ so we call subroutine passing just the stack of values, so we read from there.
\\ Inside each subroutine we make some local variables, using a way to link recordset with properties \\ property fields of recordset used 3 times, to produce different items. One is the actual object, and the \\ other two by using an index value we get field value (the default property), one as numeric and \\ the other as string.
\\ So we have to see about NULL. A NULL can exist if table schema allow it. When a field has NULL value \\ we get it as numeric type of NULL (we can't use it as a value in expressions, always return NULL), \\ but if we read it as string value we get empty string.
\\ Here we see two rows, one with a NULL phone and one with empty string (which isn't Null) \\ To examine if we have NULL we have to check the numeric variant of field.
\\ for checking the NULL we have a string function which return "NULL" or the string value, \\ including empty string.

execute "gSQL", { CREATE TABLE customers ( customer_id AUTOINCREMENT PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, birth_date date DEFAULT NULL, phone VARCHAR(50) DEFAULT NULL, address VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, state char(2) NOT NULL, points integer NOT NULL DEFAULT 0 ); INSERT INTO `customers` VALUES (1,'Babara','MacCaffrey','1986-03-28','781-932-9754','0 Sage Terrace','Waltham','MA',2273); INSERT INTO `customers` VALUES (2,'Ines','Brushfield','1986-04-13','804-427-9456','14187 Commercial Trail','Hampton','VA',947); INSERT INTO `customers` VALUES (3,'Freddi','Boagey','1985-02-07','719-724-7869','251 Springs Junction','Colorado Springs','CO',2967); INSERT INTO `customers` VALUES (4,'Ambur','Roseburgh','1974-04-14','407-231-8017','30 Arapahoe Terrace','Orlando','FL',457); INSERT INTO `customers` VALUES (5,'Clemmie','Betchley','1973-11-07',NULL,'5 Spohn Circle','Arlington','TX',3675); INSERT INTO `customers` VALUES (6,'Elka','Twiddell','1991-09-04','312-480-8498','7 Manley Drive','Chicago','IL',3073); INSERT INTO `customers` VALUES (7,'Ilene','Dowson','1964-08-30','615-641-4759','50 Lillian Crossing','Nashville','TN',1672); INSERT INTO `customers` VALUES (8,'Thacher','Naseby','1993-07-17','941-527-3977','538 Mosinee Center','Sarasota','FL',205); INSERT INTO `customers` VALUES (9,'Romola','Rumgay','1992-05-23','559-181-3744','3520 Ohio Trail','Visalia','CA',1486); INSERT INTO `customers` VALUES (10,'Levy','Mynett','1969-10-13','','68 Lawn Avenue','Atlanta','GA',796); }
\\ id 10 has no Null phone, has no phone (is an empty string) \\ Null means "no information yet" report "customers created" \\ execute a SE`LECT query to get a Recordset report "Selected customers where Points<1000 in descending order" execute "gSQL",{ SELECT customer_id, last_name, first_name, phone, points FROM `customers` WHERE Points<1000 ORDER BY Points DESC, last_name, first_name } Def CheckNull$(a, a$)=If$(type$(a)="Null"->"NULL", a$)
GetListA() ' value is already in stack
report  "Select customers id where phone is null" execute "gSQL",{ SELECT customer_id, phone FROM `customers` WHERE phone IS Null ORDER BY customer_id } GetIDwithNullPhone() ' value is already in stack

\\ using list to check the variable names list \\ there are no variables in module, at this moment. \\ All variables was local to subs, and they erased at the exit of subs \\ we can use dots in names, like a normal variable. \\ so rs.fields is a norma variable name. This variable is a PropReference type. \\ This type of variable is an object which link a property to an object \\ We can't use these variables as return values, because the use a hard link, \\ and not a standard reference to object. So PropReference can't hold alive a linked object. \\ So if we return the PropReference from where we make it then became invalid, and we have \\ to use it to raise the error.

sub GetListA(RS) if type$(RS)="Recordset" then with RS, "EOF" as new rs.eof, "fields" as new fields(), "fields" as new fields$(), "fields" as new rs.fields with rs.fields, "count" as new rs.fields.count print "Test number of fields:", rs.fields.count while not rs.eof print $(6),fields(0), print $(9)," ";fields$(1);" ";fields$(2);@(32); CheckNull$(fields(3), fields$(3));@(42), fields(4) method rs, "movenext" end while print $(0),, end if end sub sub GetIDwithNullPhone(RS) if type$(RS)="Recordset" then with RS, "EOF" as new rs.eof, "fields" as new fields(), "fields" as new fields$(), "fields" as new rs.fields with rs.fields, "count" as new rs.fields.count print "Test number of fields:", rs.fields.count while not rs.eof print $(4), fields(0), $(7), CheckNull$(fields(1), fields$(1)), $(0) method rs, "movenext" end while print end if end sub