Τρίτη, 15 Οκτωβρίου 2019

Έκδοση 9.9 Αναθεώρηση 1.

Σε αυτήν την αναθεώρηση έγιναν πολλά πράγματα. Διορθώθηκαν ατέλειες της 9.9. Μπήκαν νέα αναγνωριστικά. Ενημερώθηκε η βοήθεια. Νέα προγράμματα στο info.gsb αρχείο.

Υπάρχει το αναγνωριστικό Διαδίκτυο που γυρνάει αληθής αν έχουμε σύνδεση με το διαδίκτυο. Επίσης το Διαδίκτυο$ γυρνάει την ip που βγαίνουμε στο διαδίκτυο. Υπάρχει ένα ωραίο πρόγραμμα επίδειξης στο info.gsb το Combo1 το οποίο δείχνει παράλληλα την χρήση του combobox, και εμφανίζει συνέχεια την κατάσταση σύνδεσης μέχρι να πατήσουμε ένα πλήκτρο πάνω στη φόρμα που εμφανίζει, οπότε εμφανίζει τις δυο ip, αυτή που δίνει το router σε μας και την δημόσια ip.

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

Από το αρχείο Readme:

M2000 Interpreter and Environment

Version 9.9 revision 1

1. Update the socket and the client objects. See down4 example in info.gsb.
2. New read only variable Internet$ return the public ip or 127.0.0.1 if no internet connection exist
3. New read only variable Internet return true if there is an internet connection
4. New #Eval() and #Eval$() for tuples.
5. Report statement rewrite for use proper the TAB character when use justification. Also now is faster. Use a variant of code from internal editor to find the proper point to cut the line, using a binary search. The old wwplain function exist as wwplainOLD, for information only.
6. Improved Test form (a bug fixed).
7. Many improvments and bug fixed.
8. Many examples in info.gsb also improved, and new examples added.

Δευτέρα, 7 Οκτωβρίου 2019

Version 9.9

From readme.txt

Version 9.9 active-X
1. Remove of a hard finding bug, which in some OS leave tmp files in %temp% folder. The bug was an abnormal initialization of multimedia player. This bug cause the system to hold the tmp file until the player end. This problem not happen in Windows 8, perhaps is a bug of the external lib, for specific OS.

2. Reorganize the M2000 MessageBoxet, so now we don't get any crash at save.

3. Commands LOAD, SAVE and END check a "dirty" flag to ask for saving. Also REMOVE (with no arguments) now ask before remove the last module/function.

4. Speed improved on calculations (expression evaluation).

5. List of COM object's members, return the type of parameters, and if they are IN, OUT, IN OUT, also the return type if the member return value.

6. New objects., SOCKET, DOWNLOAD, CLIENT for use TCP/IP. Info.gsb have the Down3, a module example to show how to download asynchronous three files, using the DOWNLOAD object.

7. (This break compatibility  with previous versions). Com Events in previous versions internal passed by reference. So all variables in event service function have & before. Now only for those parameters which are ByRef we need to use &. Before we use an event we can just show the stack, and leave it as is, using the Stack statement without parameters. So we can see if we have values or references, because references in M2000 are strings -they are weak references-with name, and for events they have EV at the begin).

8. New info.gsb. Best of all is the chess game (without AI) but import and export FEN strings, and we can play chess with a friend. Also we can replay the game, or move to any previous move. Also there is a Snake board game, only an automatic version (4 players played by computer).

From info.gsb, the down3 module
The example show a running module. This module create a Form, three Download objects, with events, in an array. For this example we create a two item array, and increase by one. From internal side there are two arrays, the second one has the sink objects, the objects for events. We have a thread to display an increasing value. We can move the form, without blocking the refreshing of the form.

About the items in the picture bellow
The magenta colored form, is the M2000 console. M2000 Environment redirect all graphic and text output base on a layer. Console form has 35 layers. The Back, the Layer, and Layer 1 to 32. Here we see the Back or Background with black color behind then Layer, in magenta. The Background is a Vb6 form naked from borders and headers. The Layer is a picturebox vb6 control, the same for 32 layers above (used as "hardware" sprites, or for pages). The 35 layer is the printer page, or Printer layer. Eanch user Form, is a Vb6 form with an array of  gList4 controls. The orange header is a glist4 control. All controls like DropBox, or EditBox actually have a glist4 control. under the hood.

Vb6 (the programming language used for written M2000 interpreter and environment) not allow to place controls in a form, except for expanding a arrays of controls. But a control, from an array can send events to another object, if that object has from design time defined with that capability, for any control with the same type. So a  EditBox can place a real Glist4 control, as the "any control" and handle the events and the control.triggered by a task manager. The task manager triggered from a timer from Kernel. But because we have the interpreter in one thread actually we have a switched execution. So where the switch happen/ We have to give time to system for timer to work. When we place a refresh for a form, we do this, we stop the triggering for task manager, then we perfrom the refresh, then we start the triggering, so we place the threads when we want, not every time.

When we move the user form (handling the orange header), we see the thread to slow down (the scrolling numbers), but continue to work. So the form has another "thread layer". Because interpreter execute in console form, and events from user form "travel" to code in console form, we have a switch, where the interpreter main code stop and a service event function executed, and then the main code continue. Here for the example we don't have any event service function for the form. The form is not opened as modal. When an event occur in form, internal code watch if the event serviced, and if not, excluded from firing. If the form lost the focus and then get the focus, at the activation time, the event black list, erased, and fast constructed again.

So in the picture below we have a trigger from Kenrel, events from two forms, the console form  which are served internal, and user forms where events are for M2000 code, and we have three objects which download files from internet to a directory, sending "com" events.

I found here the cAsyncSocket (which a watch for some time),  written in Visual Basic 6 by Vladimir Vissoultchev, plus some other classes which use jthat object So the cAsynSocket handle a ws2_32.dll the interface (in binary) for Windows Sockets. Here we have a dll which send events, not by using sink objects (like com objects), but in the message loop. What is message loop? Each program which have forms have one message loop, at least. So for using window socket we need to attach it to  message loop. So every cAsyncSocket make a new window, (hidden) get events from Window Sockets, watching the message loop, and generate com objects. The Download object, isn't a ecAsyncSocket, but use one, so events from DownLoad are specific for Download, and generated inside Download. So when we see the number of bytes to increased, we get a progress event from Download, where some other events come from cAsyncSocket, where some specific messages "notify" it from Window Socket system. So behind the Download we have a system in some other thread (actual thread), which get or send bytes over internet. form and to "clients". So cAsyncSocket is a client. We get the events from Download because we have made the sink object at execution time. All the other parts are connected through design time programming. The only thing to think is how the cAsyncSocket watch the message loop. This can be done by sub-classing. Here the sub-classing is different.  Not as the subclassing with EditBox and a glist4 control. For cAsyncSocket the subclassing mean to insert a function between a function call. So when A call B we can pass a C in a way to perform a cal from  A to C and then from C to B. So we inform the A caller to call C and we get the information that A actual want to call B, so we get the B address to call, before setting the A caller to call C. Before we close the thing, we have to restore the sub-classing. This is the idea. If something in C not work fast, then slow down the message loop. For M2000 interpreter there is a programming "switch" who prevents the ghosting screen, which tell us about "the program not responding".  Windows send messages and waiting to return back to check if a message loop respond. So a message has a time stamp. Using the anti ghosting state M2000 may run programs with no refreshing for long time, without problem. But here we want the 3 downloads to work, connecting Internet (other PC, in the World Net, not a Local Net), and not blocking the "actuator", which are us, where we place the three methods, one for each object, for the downloads.

So when we get the progress event, at a speed of a modern PC, the sink created by M2000 interpreter, fire the event, calling a specific function (all the com objects send events, from a specific type sink object which call a specific function give the parameters, of which object send and what, the name of event, and the parameters. At the return of this function the param list send it back, to work for by reference type variables). So the events from com objects just interrupt the main thread when happen, not when the main thread ask a "doevents", So a thread (a M2000 thread) may interrupted, very fast. Because the functions where we serve services, in M2000 are different from normal functions, to access the module's variables/functions/modules (they called using a Call Local, passing the module's name as the name of function, so a X in module is a X in function), we have to make something to ensure that parameters from the call are written in different variables, but with same name. So we declare New before the name's to read from stack of values (each call from an event has own stack of values, so the serving function change name with module name, only to look/handle entities from module.  Normal functions are closed as the modules, only local or global entities are visible. If a Module A has a local Module B, then B can't see A members, and A can't see B members.

Another thing to see here is about layers, and the split screen. On way to change split screen row, is by setting the second  parameter in CLS command. So a CLS ,5 set 6th row as the top row of the down part of screen, then scrolling part. here we set at some stages different split screen row, so the last setting leave the part with numbers.

The printing routine on Layers use text with transparent background. Except the Print Over, which erase the line before print. So we can easy use it to display information to upper part  of split screen, the static part. We have to erase before print, and that happen by  Print Over.

Print Over, Print Part and Print Under are a set of print commands, which have some properties. All of this commands restore colors, columns, print style setting. No one of them insert a new line after the call. Print Under draw a line (as underline) to current line, then insert a new line, then print anything we have as argument list. Print Over erase the line except the horizontal line which Print Under draw. Aldo Print Over set by default one column entire width, and print style 6 (proportional text, center justify). In the picture bellow we see print over three times for progress on console and three for the form. Each time we have change the one column all row, to specific columns.





In info.gsb they are these two modules - games. The Snake (auto play), and Chess for two. The first (snake) use Greek statements, the other English (M2000 has two set of commands, Greek and the English, a command has to be in one language, but we can use commands from any language anywhere in M2000).

The Snake handle the parts of games in any console configuration, here we see the cyan BACK and a print using Console as Layer


Chess is more advanced. The console now is the lines under the chess board. The chess board and the header are drawing in the BACK layer. The BACK layer can be extended to any width and height (respecting then top limits), the LAYER and the 32 layers from LAYER 1 to LAYER 32 (when used as consoles)  they have width a multiple of then base width. Also the height have rows plus line space so for proper handle we get a height smaller than total visible height. But this not happpem fro Back Layer.
For joke we can move the form by holding mouse left button on header. And each time we run the module, form a random number the program decide to change size....
Chess record each position in FEN string.We can go back to any previous move. We can replay the game, We can export the game (we can import too).


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

Chess Board Example

This example use Unicode chess characters. Look the print screen, at the background of  these characters on the board. Normaly the color inside are the same as the font, because only the line of font drawing. So to have white inside pawns, we have to print the characters in white background using a non antialliasing printing (so we get black pixels for black colored characters), and we have to use flood fil in a pixel outside the chess character to fill the square with desired color.

for each board we feed a Forsyth–Edwards Notation (FEN) a string which define a board position.



Just copy this and paste to a module a empty code block.
Cls 15
Pen 0
Form 40,32
Gradient 3,5
Def White$="PNBRQK", Black$="pnbrqk", WhiteDisp$="♙♘♗♖♕♔"
Def BlackDisp$="♟♞♝♜♛♚", empty$="12345678"
Dim emptydisp$(1 to 8)
for i=1 to 8 :emptydisp$(i)=string$(" ",i):next i
Def board$, status$, oldI
dim line$()
Double
OldI=Italic
Italic 1
Pen 14 {Report 2, "Chess Board Example"}
Italic OldI
Normal
SetBoard("rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2")
DrawBoard(1000, 2000, 600, 15)
SetBoard("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
DrawBoard(11500, 2000, 300, 15)
SetBoard("rnbqkbnr/pp1ppppp/2p5/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
DrawBoard(11500, 8000, 180, 15)
Push Key$
Drop
Cls
end 
Sub DrawBoard(leftmargin, topmargin, squarewidth, labelcolor)
Refresh 10000
Local a=true, z=bold : bold 0
Local l=squarewidth, k=2*l, k1=k*.85, N1=6, N=6, M=4, B=k*8, B1
Local d=0
Repeat
N=N1
N1+=.25
Until K1<size.Y("A", "Arial", N1)
topmargin-=l
leftmargin-=l
move leftmargin+l,topmargin+l
B1=(l div 300)*twipsX
step -B1,-B1
B+=B1*2
Width b1 div 2+1 {
color #666688 , 1{Polygon 0, B, 0, 0, B, -B, 0, 0, -B}
}


M=N*.65
For i=1 to 9
 d=leftmargin
 move d, topmargin
 if i<9 then
  step 0, k
  Pen labelcolor{Legend str$(9-i,""), "Arial", M,0,2}
  step 0, -k
 else
  N=M
 end if
 for j=1 to 8
  If i<9 then
   step l, l
   color  #666688,1 {fill k,k, 15}
   step  -l, -l
   \\ Last parameter is 0 for no antialliasing
   Legend mid$(line$(i),j,1), "Arial", N, 0, 2,0
   step l-twipsX
   if a then floodfill  ,,#666688 else floodfill , , #ccddff
   a~
  else
   step k, k
   pen labelcolor {
    Legend mid$(line$(i),j,1), "Arial", N, 0, 2
   }
  End If
  d+=k
  move d, topmargin
 next
 a~ : topmargin+=k
next
bold z
Refresh 60


end sub
Sub SetBoard(chessboard$)
 board$=leftpart$(chessboard$," ")
 status$=rightpart$(chessboard$," ")
 nl$={
 }
 for i=1 to 6
 board$=replace$(mid$(White$,i,1),mid$(WhiteDisp$,i,1), board$)
 board$=replace$(mid$(Black$,i,1),mid$(BlackDisp$,i,1), board$)
 next
 for j=1 to 8
 board$=replace$(str$(j,""),emptydisp$(j), board$)
 next
 line$()=piece$(board$,"/")
 dim line$(1 to 9)
 line$(9)="abcdefgh"
End Sub

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

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

Διορθώσεις πάνω στις τελευταίες αλλαγές. Επίσης μπήκαν δυο νέα τμήματα στο info.gsb, το ένα κάνει εξαγωγή του ελληνικού κειμένου βοήθειας από τη βάση δεδομένων ή του αγγλικού (διαλέγουμε). Το ελληνικό κείμενο είναι πάνω από διακόσιες σελίδες Α4. Το δεύτερο τμήμα είναι ένα πρόγραμμα για να αλλάζουμε το κείμενο απευθείας στη βάση δεδομένων.

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

Ανάκτηση αρχείου σε αστοχία εκτέλεσης κατά την αποθήκευση.

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

Στην έκδοση 9.8 αναθεώρηση 45 δεν έχει βρεθεί ακόμα το bug (σχετίζεται με μια ασύγχρονη λειτουργία). Όμως έχει ξεπεραστεί το πρόβλημα με το σύστημα της πρόσκαιρης αποθήκευσης πριν την ερώτηση αν θέλουμε ή όχι να αποθηκεύσουμε πάνω στο παλιό.
Ισχύει ότι η αποθήκευση δημιουργεί ένα αρχείο τύπου bck για το παλιό αρχείο.

Πώς γίνεται η ανάκτηση:
Όταν λοιπόν πάμε να σώσουμε πρώτα σώνει το αρχείο με κατάληξη gsb1 και αν πέσει το σύστημα τότε: ανοίγουμε τον διερμηνευτή και φορτώνουμε το αρχείο, και το gsb1 αυτόματα μετονομάζεται με κατάληξη gsb. Στην ουσία αλλάζει τύπο στο gsb ως bck1 και γράφει το νέο στο gsb1 πριν το ερώτημα ώστε αν δηλώσουμε ότι δεν θέλουμε να γράψουμε στο αρχείο τότε το bck1 γίνεται πάλι gsb και διαγράφεται το gsb1.

Έχω προσπαθήσει να βρω το πρόβλημα χωρίς επιτυχία. Οι προσπάθειες συνεχίζονται. Σπάνια συμβαίνει αλλά.

Όταν με την Φόρτωση ή τη Load φορτώσουμε ένα αρχείο τότε αν αυτό υπάρχει το φορτώνει, αν όχι κοιτάει αν υπάρχει με κατάληξη gsb1 και αν ναι τότε πρώτα του αλλάζει τύπο σε gsb και μετά το φορτώνει.


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

Εικόνα από την νέα έκδοση:


Σε αυτήν την αναθεώρηση:

1. Διορθώθηκε ο κώδικας εξαγωγής σε html, οπότε το </ γράφεται σωστά, και το βλέπουμε εδώ (είναι αντιγραφή κειμένου από τον htmleditor που υπάρχει στο info.gsb - υπάρχει στο setup).

<!DOCTYPE html>
<html lang = "en-US">
  <head>
    <meta charset = "UTF-8">
    <title>externalStyle.html</title>
    <link rel = "stylesheet"
          type = "text/css"
          href = "myStyle.css" />
  </head>
  <body>
    <h1>External Style</h1>
    <p>
      This page has styles set for paragraphs, body, and header 1.
    </p>
    <p>
      The styles are defined in an external style sheet.
    </p>
  </body>
</html>
2. Δυο νέες ιδιότητες για το EditBox. Η HighlighParagraph με τιμή True κάνει την παράγραφο που έχουμε το δρομέα να "φωτίζεται". Το χρώμα του φόντου για το "φώτισμα" το παίρνει από την HighlightColor.  Η χρώμα(0χ33, 0χ33, 0χ33) ή το #333333 δίνει αρνητικό νούμερο οπότε βάζουμε ένα - πριν για να δώσουμε τη θετική τιμή. Η Μ2000 χρησιμοποιεί τρία σετ χρωμάτων και τα ξεχωρίζει με τα πρόσημα και τη περιοχή τιμής: RGB σετ 0 έως -2^24-1, Windows Colors 0 to 15, και System Colors από 0χ80000000. Δεκαεξαδικά νούμερα γράφονται με 0χ ή 0x, ως αριθμοί χωρίς πρόσημο, και με τα & και % στο τέλος όταν έχουν πρόσημο για 32 και 16bit. Έτσι το 0χFFFF% είναι το -1 σε 16 bit. To #AABBCC (html χρώμα) είναι αυτό -0χCCBBAA, διότι το AA είναι στη θέση για το κόκκινο και γράφεται στη χαμηλή θέση στη μνήμη (little endian), άρα στο δεκαεξαδικό νούμερο θα είναι το πιο δεξιό κομμάτι. Αλλά σαν html χρώμα θέλουμε να βλέπουμε το RGB, δηλαδή το κόκκινο με τιμή 0χAA να είναι στα αριστερά.

Στην αναθεώρηση 44:

1. Αφαιρέθηκε ένα σφάλμα που έκανε τις φόρμες χρήστη να μην ανοίγουν από την γραμμή εργασιών (Task Bar), αν τις κάναμε minimized. (δεν άνοιγαν ούτε με ctrl-tab), μπορούσαν μόνο να κλείσουν από την taskbar.
2. Μεγάλη βελτίωση στους διορθωτές (τον βασικό πάνω στην κονσόλα της Μ2000, και το EditBox το στοιχείο ελέγχου για φόρμες χρήστη.
Οι φόρμες χρήση δουλεύουν με ένα βασικό στοιχείο ελέγχου και πάνω σε αυτό άλλα αντικείμενα του αλλάζουν την χρήση. Το βασικό είναι ένα list box. Αυτό γίνεται και επεξεργαστής κειμένου, με σύστημα για χρωματισμό κειμένου/κώδικα.
Σε αυτήν την αναθεώρηση μπήκε το Ctrl πάνω στα βελάκια ή στο delete ή στο Backspace (τα δυο τελευταία είδα ότι "άλλαξαν" αφού έφτιαξα το σύστημα για τα βελάκια. Με το ctrl και τα βελάκια αριστερά και δεξιά μετακινούμε το δρομέα βάσει του περιεχόμενου. Σκοπός είναι να μετακινούμε το δρομέα με ένα κλικ από το τέλος μιας λέξης μέχρι την αρχή της και το ανάποδο, καθώς και να περνάμε διαστήματα, είτε προς τα αριστερά είτε προς τα δεξιά. Επιπλέον σε παρενθέσεις και άλλα σύμβολα θέλουμε ξεχωριστή στάση. Μπορούμε να συνδυάσουμε και το SHIFT, ώστε να μαρκάρουμε κατά την μετακίνηση (μπορούσαμε και πριν, αλλά τώρα με πολύ γρήγορο τρόπο γιατί ο δρομέας με μια κίνηση πάει στην επόμενη στάση, όπως ορίζεται από την λογική του προγράμματος.

Επίσης με τα F2 και F3 (αναζήτηση προς την αρχή και προς το τέλος)  τώρα έχουν έναν εσωτερικό διακόπτη που αρχικά ορίζει η αναζήτηση να γίνεται με λέξεις. Για να αναζητήσουμε κάτι απλά το μαρκάρουμε στο κείμενο,. αν υπάρχει ήδη ή με τα shift και control επιλέγουμε την αναζήτηση βάσει ενός πεδίου εισαγωγής σε μια φόρμα διαλόγου. Με το Shift και το F2 ή F3 η αναζήτηση γίνεται με βάσει ότι δίνουμε, ακόμα και μέρος λέξης. Με το Ctrl και το F2 ή F3 η αναζήτηση γίνεται για μεταβλητές, και γενικά για ονόματα. Σε αυτήν την αναθεώρηση η χρήση των   F2 ή F3 ακολουθεί το τελευταίο  τρόπο αναζήτησης, ενώ ξεκινάει με αναζήτηση λέξεων.

Επίσης άλλη αλλαγή (που μερικώς είχε γίνει στο προηγούμενη αναθεώρηση) έγινε στο τρόπο 'απασχόλησης" του εγγράφου εντός του EditBox, ώστε σε περίπτωση αντικατάστασης λέξης ή γραμμάτων (F5 και shift F5) να μπορεί η φόρμα να χρησιμοποιείται (να παίρνει "γεγονότα"). Αυτό το έκανα με χρήση ενός ρολογιού υψηλής ανάλυσης, ανά αντικείμενο EditBox, όπου κατά την διάρκεια της επανάληψης της αναζήτησης ελέγχει το πρόγραμμα αν ένα χρονικό διάστημα έχει περάσει για να δώσει χρόνο στο σύστημα. Ενώ στον διορθωτή για την Μ2000 στην κονσόλα για μικρά κείμενα εμφανίζονται οι αλλαγές με διαδοχική κίνηση του κειμένου, στο EditoBox η αλλαγή γίνεται ακαριαία.

Και οι δυο κειμενογράφοι ΄(έχουν σε μεγάλο βαθμό όμοιο κώδικα, όμως ο κειμενογράφος της κονσόλας ανήκει σε μαι σταθερή φόρμα, ενώ το Editbox μπορούμε να το βάλουμε όσες φορές θέλουμε πάνω σε μια ή περισσότερες φόρμες - Η Μ2000 υποστηρίζει πίνακες; φορμών και πίνακες στοιχείων οπότε τα γεγονότα από ένα πίνακα φορμών με πίνακες στοιχείων θα έχει αρχικά δυο τιμές, τον αριθμός φόρμας και τον αριθμό στοιχείου (και η φόρμα και το στοιχείο έχουν ονόματα, άρα μπορούμε να έχουμε και άλλα σετ φορμών-στοιχείων ή φορμών-στοιχείωνΑ-στοιχείωνΝ ή μιας φόρμας με πολλούς πίνακες στοιχείων.

Αύξησα τις δυνατότητες χρωματισμού. Τώρα χρωματίζει και HTML, πράγμα αρκετά δύσκολο, γιατί η γλώσσα αυτή έχει tags, ενώ δεν έχει αλλαγές γραμμών (μπορούν να μπουν οπουδήποτε αλλά δεν μετράνε). Στη προηγούμενη αναθεώρηση είχα δώσει μια πρώτη έκδοση. Τώρα ήρθε η πιο βελτιωμένη. Επίσης μπορούμε να αλλάξουμε τα χρώματα τόσο του EditBox τα βασικά όσο και του χρωματισμού. Ενώ υπήρχαν δυο σετ χρωμάτων, για φωτεινό και για σκούρο φόντο, τώρα μπήκε και ένα προγραμματιζόμενο. Σε κάθε περίπτωση η αντιγραφή στο πρόχειρο (που δίνει δυο πράγματα, κείμενο και Html με χρωματισμένο κώδικα) ακολουθεί ένα από τα δύο βασικά, ανάλογα με τι έχουμε δώσει στις ρυθμίσεις. Με το htmleditor θα πάρουμε χρωματισμένο html σε κωδικοποίηση html, και μπορούμε να το επικολλήσουμε είτε σε επεξεργαστή κειμένου είτε σε blog, όπως αυτό εδώ.

float
float

Σάββατο, 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