Τρίτη, 31 Μαΐου 2016

Αναθεώρηση 23 (έκδοση 8.1)

Στην νέα αναθεώρηση έφτιαξα τις προαιρετικές παραμέτρους (optional). Για άλλα θέματα σχετικά με την αναθεώρηση δείτε στο τέλος του κειμένου.
Στη Μ2000 δεν προκαθορίζουμε στο διερμηνευτή τις παραμέτρους στις Συναρτήσεις και τα Τμήματα. Και στις δυο περιπτώσεις μπαίνουν ότι δώσουμε στο σωρό τιμών και καλείται ανάλογα το τμήμα ή η συνάρτηση. Εκεί είναι ο προγραμματιστής "υπεύθυνος" για το τι θα πάρει και πώς.
Με τον μέχρι χθες διερμηνευτή δεν μπορούσαμε να μην δώσουμε μια ενδιάμεση τιμή, στη τμημάτων-συναρτήσεων. Δεν υπήρχε τρόπος να φανεί ότι δόθηκε "μη τιμή".
Από την αναθεώρηση 23 (έκδοση 8.1) έγινε το εξής. Μπήκε ένα ψεύτικο στοιχείο που λέει "προαιρετικό" ([optional]), για το σωρό τιμών. Επιπλέον μπορούμε στην Διάβασε να χρησιμοποιήσουμε το ? που δηλώνει ότι αν δεν βρει τιμές τότε αν υπάρχουν οι μεταβλητές τις αφήνει ως έχουν αλλιώς τις δημιουργεί με την στάνταρ αρχική τιμή.
Επιπλέον το ? μπορεί να δοθεί και ως τιμή στη κλήση τμημάτων-συναρτήσεων και αυτό βάζει στο σωρό το ψεύτικο στοιχείο [optional]. Αν ζητάμε δείκτη από το σωρό και υπάρχει το [optional] θα πάρουμε λάθος.

Παρακάτω το πρόγραμμα είναι στα αγγλικά, και κάνει τα εξής:

  • Δοκιμάζουμε το alfa με το ? ενώ δεν έχουμε δηλώσει στη Read ότι μπορεί να λείπει κάποια τιμή.  Εδώ αν λείπει τιμή τότε βγαίνει λάθος. Οπότε αν δεν θέλουμε να δώσουμε τιμή, βάζουμε το  ?
  • Στο τμήμα beta ορίσαμε να είναι προαιρετικό το διάβασμα! Εδώ πρέπει να αντιληφθεί ο αναγνώστης ότι τα τμήματα καλούνται με κοινό σωρό τιμών, οπότε αν δεν βάλουμε τιμές στη κλήση του τμήματος, μπορεί να έχει τιμές από πριν ο σωρός, άρα να πάρει τελικά τιμές η beta. Εδώ ξέρουμε ότι δεν έχει  (η flush ή Άδειασε αδειάζει το σωρό τιμών, που είναι μια ειδική στοίβα στη Μ2000). Όπως και να καλέσουμε την beta, θα αφήνει αρχικές τιμές όπου δεν παίρνει τιμές! Το ? λέει στη Διάβασε πως αν είναι κενός ο σωρός τότε να αφήνει ότι τιμές έχουν όσες μεταβλητές υπάρχουν, αλλιώς φτιάχνει νέες με μηδενικές τιμές.
  • Στο delta διαβάζουμε μια τιμή με αναφορά και παίρνουμε ή όχι μια προαιρετική τιμή Χ. Δοκιμάζουμε να περάσουμε το ? ενώ ζητάει αναφορά, και παίρνουμε λάθος στην Try { } (λέγεται Δες { } στα ελληνικά - οι λέξεις που χρησιμοποίησα έχουν σχετικό νόημα για την περίπτωση)
  • Μετά φτιάχνω μια συνάρτηση alfa(), μπορώ να το κάνω ακόμα και αν υπάρχει τμήμα alfa, όπου χρησιμοποιώ στη διάβασε το "?"
  • Αμέσως μετά φτιάχνω μια beta (θα μπορούσα να την κάνω alfa, δηλαδή να αλλάξω τον ορισμό της alfa με νέο ορισμό). Εδώ η Διάβασε δεν έχει το "?". Ενώ πριν κάλεσα τη συνάρτηση με alfa(), χωρίς παράμετρο, εδώ έπρεπε να βάλω το  "?" για να μπορεί ο διερμηνευτής στη Beta να αφήσει την τιμή που είχε η Χ. Το "?" αποτελεί κάτι, άρα δεν βγαίνει λάθος το Read ( η Διάβασε στα Ελληνικά)
(Στη ΓΛΩΣΣΑ του σχολείου η Διάβασε διαβάζει από το πληκτρολόγιο. Το αντίστοιχο στη Μ2000 είναι η Εισαγωγή ή Input)

flush
module alfa {
      let a=1000, b=23
      Read a, b
      Print a, b
}
\\ we say all not optional
\\ but predefined with a default value each
alfa ?, ?
alfa 44, ?

module beta {
      let a=1000, b=23
      \\ we say all optionals
      read ? a, b
      Print a, b
}
Print "beta Test"
beta 1,2
beta
beta ,2
beta 1
\\ we can use ? here
beta ? , ?

Module delta {
        X=-1
       Read ? &name$, X
        if X>0 then name$=name$+", nice day"
}
b$="Hello"
delta &b$
Print b$
b$="Hello"
delta &b$, 1
Print b$
\\ we can't pass predefined or optional for reference
\\ error trapping
try { delta ? }
if error then report error$
Function alfa { X=10 : read ? X : =X**2}
Print alfa(), alfa(2) \\ 100  4
Function beta {X=10: read X : =X**2}
Print alfa(?), alfa(2) \\ 100  4



Νέες Προσθήκες
Διορθώθηκε το σύστημα λήψης γεγονότων από ActiveX ώστε να μη χρειάζεται να έχουμε γενικές μεταβλητές (δείτε στη προηγούμενη ανάρτηση έχω αλλάξει το πρόγραμμα).
Εξ ορισμού σε μια κατάσταση ο τρόπος ταξινόμησης βασίζεται στο κείμενο. Μπορούμε να ορίσουμε Binary ή Database, με κλήση μεθόδου, και περισσότερες πληροφορίες θα δοθούν αργότερα.
Μπήκε νέο σύστημα εύρεσης αντικειμένων τύπου ActiveX.
Προσοχή, δουλεύει μεν, αλλά "Τρώει μνήμη", δηλαδή δεν έχει τελειοποιηθεί ακόμα
Φυσικά μπορούμε να δουλεύουμε χωρίς τα GUID, και χρησιμοποιώντας το internet για να βρούμε τις μεθόδους, και τις ιδιότητες!


\\ to get this GUID we need to perform a LIST LIB TO A
\\ where A is a module (run this in M2000 command line)
\\ not all the objects are creatable
Declare Worksheet "{00020820-0000-0000-C000-000000000046}"
\\ we make an inventory
\\ we can write: Inventory alfa
\\ but param() with an object return an inventory
\\ keys are all names in capitals, and items are the definitions (a simple form)
\\ for properties and functions
alfa=param(Worksheet)
Report Type$(Worksheet) ' Workbook
For i=0 to len(alfa)-1
Report 3, alfa$(i!) ' use index, not key
Next i

\\ we can write here Declare Worksheet Nothing
\\ But at the end all objects (ActiveX) erased



Σε άλλο τμήμα γράφουμε αυτό
\\ report to screen
list lib
\\ report the exe or dll or ocx file
\\ and all CoClasses

\\ menu$() has all the names
For i=1 to menuitems
      Report Menu$(i)
Next i

Κυριακή, 29 Μαΐου 2016

Αναθεώρηση 22 (Έκδοση 8.1)

Σχεδόν τελείωσε η αναβάθμιση της Μ2000. Μου έχει μείνει ο Assembler. Έχω βρει ένα ήδη γραμμένο σε VB6 assembler, αλλά θα τον δοκιμάσω πρώτα! Πάντως για την ώρα έχω δει ότι μπορώ να τρέξω κώδικα ενώ ισχύει το DEP (απαγόρευση εκτέλεσης, σε κεντρικό χώρο, αλλά όχι σε εικονικό).


Το παρακάτω πρόγραμμα Ανοίγει το Word. Αν κλείσουμε το Word θα μας πεις σε πόσα δευτερόλεπτα το κλείσαμε. Αν ο χρόνος περάσει τα 10 δευτερόλεπτα θα κλείσει το Word (εκτός και αν έχουμε κάποιο διάλογο ανοικτό).


Αναθεώρηση 23, δεν χρειάζεται να είναι Gobal το ExitNow. Η κλήση του γεγονότος γίνεται σαν να είναι στο τμήμα.

ExitNow=false
Declare WithEvents Alfa "WORD.APPLICATION"
Title "Minimized", 0
Wait 300
Print "ok"
With Alfa, "Visible", True
Function ALFA_QUIT {
            Print "Why you close Word?"
            ExitNow=True
}
M=0
Every 20 {
      If ExitNow then exit
      M++
      If M>500 then exit
}
Try {
      Method Alfa, "QUIT"
}
Declare Alfa Nothing
if ExitNow then {
      Print format$("Finish  {0:2} sec", M/50)
} Else {
      Print "Close Word manually"
}
Title "ok",1

Τετάρτη, 25 Μαΐου 2016

Benchmark - Μέτρηση ταχύτητας σε εύρεση στοιχείου.

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

Ήδη με την μικρή λίστα τιμών, το πρώτο πρόγραμμα έχει δεκαπλάσια ταχύτητα. Όσο αυξάνονται τα στοιχεία, το πρώτο πρόγραμμα δεν αργεί παραπάνω, ενώ το δεύτερο επειδή κάνει γραμμική αναζήτηση εξαρτάται κάθε φορά από τον αριθμό των στοιχείων και την θέση που βρίσκεται αυτό.



Κατάσταση αλφα = "Two","Three","One","Four","Five"
Προσθήκη αλφα, "George Hello"
Προσθήκη αλφα, "Hello"
Προσθήκη αλφα, "1"
Προσθήκη αλφα, "Zero", "Mark"
Ταξινόμηση αλφα
Τύπωσε $(4)
Για ι=0 έως Μήκος(αλφα)-1 {
      Τύπωσε αλφα$(ι!), ι
}
Αν Υπάρχει(αλφα,"Hello") τότε {
      Τύπωσε "Θέση στο άλφα=";έκφρ(αλφα!)
}
Αν Υπάρχει(αλφα,"Zero") τότε {
      Τύπωσε "Θέση στο άλφα=";έκφρ(αλφα!)
}
Αν Δεν Υπάρχει(αλφα,"Zero123") τότε {
      Τύπωσε "Δεν βρέθηκε Zero123"
}
Χ=0
Αναλυτής
Για μ=1 έως 1000 {
      Χ=Υπάρχει(αλφα,"Hello")
      Χ=Υπάρχει(αλφα,"Zero")
      Χ=Υπάρχει(αλφα,"Zero123")
}
Τύπωσε φόρτος



Συνάρτηση ΒρεςΠρώτο {
      Διάβασε &α$, β$
      Ν=0 : Ζ=0
      Επανέλαβε {
            Χ=0 : Ν++
            Εύρεση α$,β$, Ν
            Διαβασε Ν
            Αν Ν=0 τότε έξοδος
            Διάβασε Χ, Ζ
      }  Μέχρι Ζ=1
      =Χ
}
Έγγραφο αλφα$ = {Two
Three
One
Four
Five
}
αλφα$={George Hello
}
αλφα$={Hello
}
αλφα$={1
}
αλφα$={Zero
Mark}
Ταξινόμηση αλφα$
\\ Αναφορά αλφα$
Τύπωσε $(4)
Για ι=1 έως εγγράφου.παρ(αλφα$)
Τύπωσε Παράγραφος$(αλφα$, ι), ι-1
Επόμενο ι
Χ=ΒρεςΠρώτο(&αλφα$,"Hello")
Αν Χ>0 τότε Τύπωσε "Θέση στο αλφα$="; Χ-1
Χ=ΒρεςΠρώτο(&αλφα$,"Zero")
Αν Χ>0 τότε Τύπωσε "Θέση στο αλφα$="; Χ-1
Χ=ΒρεςΠρώτο(&αλφα$,"Zero123")
Αν Χ=0 Τότε Τύπωσε "Δεν βρέθηκε Zero123"


Αναλυτής
Για μ=1 έως 1000 {
      Χ=ΒρεςΠρώτο(&αλφα$,"Hello")
      Χ=ΒρεςΠρώτο(&αλφα$,"Zero")
      Χ=ΒρεςΠρώτο(&αλφα$,"Zero123")
}
Τύπωσε φόρτος


Παρασκευή, 20 Μαΐου 2016

Αναθεώρηση 20 (Έκδοση 8.1) Μετατροπή σε dll

Αποφάσισα να κάνω την γλώσσα να τρέχει σε άλλες γλώσσες ως αντικείμενο, καθώς επίσης και η ίδια να ανοίγει άλλες γλώσσες (script) και να δίνει το περιβάλλον της ως αντικείμενο.

Εδώ ανοίγουμε τη javascript μέσα από την Μ2000, και αυτή καλεί την Μ2000,


clear
declare vs "MSScriptControl.ScriptControl"
declare Alfa Module
Print Type$(Alfa)
With vs, "Language","Jscript", "AllowUI", true
Method vs, "Reset"
Print Type$(Alfa)
Method vs, "AddObject", "M2000", Alfa
method vs, "ExecuteStatement", {
            M2000.AddExecCode("Function BB {=1234 **number} : k=2");
            M=M2000.ExecuteStatement("Print 1234, BB(k)");
            M2000.AddExecCode("aa$=key$");
}
Method vs, "eval", {"hello there"} as X$
Print X$
Method vs, "eval", {"hello there too"} as X$
Print X$
list
declare vs nothing




Το ίδιο Script Control δίνει  και VBScript. Φτιάχνουμε μια Sub που τρέχει σε VBScript αλλά τυπώνει στην Μ2000, στην κονσόλα της!


clear
declare vs "MSScriptControl.ScriptControl"
declare Alfa Module
Print Type$(Alfa)
With vs, "Language","Vbscript", "AllowUI", true
Method vs, "Reset"
Method vs, "AddObject", "M2000", Alfa
X=0
Method vs, "AddCode",{
      sub mine(b)
            for i=1 to b
                  M2000.AddExecCode "Print "+cstr(i) +","
            next
            M2000.AddExecCode "X="+cstr(123456)
      end sub
}
method vs, "ExecuteStatement", "mine 50"
Print X
declare vs nothing



Επίσης τώρα υπάρχει Installer (Inno Setup) που βάζει και εικονίδιο στην επιφάνεια εργασίας και κάνει το απαραίτητο Regsvr32 στο m2000.dll


Παρακάτω έχουμε την περίπτωση σύνδεσης με το m2000.dll το οποίο δίνει ένα αντικείμενο M2000.callback

Σε VB6
' at module level
Dim m As New M2000.callback


' in a load or sub main
m.Run "start"


' We can start with clear console, program, variables (but not stack, use Flush)
' Clear for variables and New for clear program and static variables
Dim a$
If m.Status < 0 Then Exit Sub
m.ExecuteStatement "Start"
again:
If m.Status = 0 Then


m.Run "show :repeat { clear cmd$ :print $(0), {M2000>}; : line input cmd$ : print" + vbCrLf + " inline cmd$" + vbCrLf + "} always", False


End If
If Abs(m.Status) = 1 Then
' Use m.ErrorEn for errors in english
a$ = m.ErrorGr
m.Reset
m.Run "Print : Print {" + a$ + "}"
GoTo again
End If
F1.Caption = m.Eval(CStr(Timer))
m.ShowGui = False
m.Reset


Debug.Print "ok".




Άλλη περίπτωση:
μπορούμε να πάρουμε εξαγωγή από παράσταση
a= m.Eval("100*500")


Γρήγορο άνοιγμα:

Sub cli()
If m.Status = 0 Then
'm.cli "", "CLI>"
m.cli "c:\note1.gsb"
m.Reset
End If
End Sub


ή

Sub cli()
If m.Status = 0 Then
m.cli "", "CLI>"
m.Reset
End If
End Sub








Τρίτη, 17 Μαΐου 2016

Αναθεώρηση 18 (Union σε Structures)

Σε αυτή την αναθεώρηση διόρθωσα και bug (άρα καλό είναι να κατεβάσει κανείς την τελευταία έκδοση).

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

structure alfa {
           {
                  low as integer
                  high as integer
            }   
            value as long
}

Buffer Clear one as alfa *100
Return one, 20!Value:=0xFFFFAACC
Hex Eval(one, 20!high),"", Eval(one, 20!Low), "",Eval(one, 20!value)

Στο παράδειγμα παραπάνω έχουμε ορίσει το alfa να το διαβάζουμε και σαν long (unsign long) και σαν integer (unsign integer) από τα δυο μισά. Η εντολή Hex είναι η Print αλλά για δεκαεξαδικό (unsign, όπου βρει αρνητικούς ή μεγαλύτερο από 0xFFFFFFFF τότε εμφανίζει αντί για αριθμό ένδειξη λάθους --- ή +++). Δείτε ότι το High όχι μόνο του λέει πόσο  θα μετατεθεί από το 20 *4 που είναι η μετάθεση από την θέση του πρώτου ψηφίου (Byte) αλλά και το μέγεθος που θα πάρει, εδώ τον ακέραιο των 2 Bytes (ψηφίων).

Τι θα γίνει όμως αν βάλουμε κάτι πίσω από το Value (ενώ τώρα Value και Low είναι ίδιες μεταθέσεις, δείχνουν στο 0)

Για να γίνει πιο ωραίο, βάζουμε το value ως πίνακα 20 long (Μακρύς). Τα μέρη που απαρτίζουν την ένωση, "ξεχειλώνουν" όσο το μεγαλύτερο. Όμως στο πρώτο μέρος, με τα Low και High έχουμε μεταθέσεις από την αρχή. Πώς θα βρούμε ας πούμε το 3 στοιχείο. Και αν υποθέσουμε ότι έχουμε 100 από το alfa, τότε ας βάλουμε στόχο να βρούμε το alfa[10]high[3], alfa[10]low[3], alfa[10]Value[3].
Εδώ το πρόβλημα λύνεται με μια συνάρτηση! το Value[3] έχει μεν το 3 αλλά αυτό δεν είναι καθαρός αριθμός, έχει μονάδες, έχει το Long το οποίο είναι 4*byte. Και το low[3] έχει και αυτό μονάδες είναι το Integer ή το 2*byte. Για να λειτουργήσει εδώ σωστά η μετάθεση πρέπει για κάθε πραγματική μετάθεση στο value να έχουμε ισοδύναμη στο low, και ομοίως και στο high. Αν είχαμε Long στο παράλληλο, ή κατ΄ένωση μέρος, τότε θα ένα προς ένα ισοδυναμία. άρα το 3 στο ένα και το 3 στο άλλο θα ήταν ταυτόσημα. Τώρα χρειάζεται η συνάρτηση Mult() που μας δίνει το νούμερο για πολλαπλασιασμό. Αυτό βγαίνει από την αναλογία, 4 προς 2 και έτσι μας δίνει τον παράγοντα 2 που πολλαπλασιάζουμε στο 3 στο Low. Το 30!high!mul*M g είναι στην ουσία:
To M=3 επειδή ζητάμε το 4o στοιχείο (στο 0 είναι το πρώτο)
Δείκτης βάσης+30*μέγεθος(alfa)+μετάθεση(high)+μέγεθος(high)*2*3
Ο τύπος στο mult() δεν είναι γενικός. Μας βολεύει εδώ γιατί το μέγεθος 4 που επαναλαμβάνεται είναι το άθροισμα του μήκους σε bytes (bytes) των δυο ακεραίων (integer), και τόσο είναι και το μήκος του μακρύ ή long στο δεύτερο μέρος (και τα δυο μέρη δείχνουν την ίδια μνήμη, ο τρόπος που διαβάζεται και γράφεται έχει να κάνει με την χρήση του offset, ή μετάθεσης). Αν βάζαμε στο low as integer πολλαπλασιαστή έστω 20 τότε αντί να έχουμε το high ακριβώς 2 Byte μετά θα το έχουμε 2*20, ή 40 byte μετά. Αν μάλιστα στο long δεν βάζαμε το *20 αλλά το βάζαμε στο high, τότε και το value θα ήταν εκ των πραγμάτων  *20 εκτός να προσθέταμε και κάτι άλλο.



structure alfa {
            base1 as integer*30
           {
                  low as integer
                  high as integer
            }   
            \\ now we extend to 20 items as Long or High-Low integers
            value as long*20
}
Buffer Clear one as alfa *100
Return one, 20!Value:=0xFFFFAACC
Hex Eval(one, 20!high),"", Eval(one, 20!Low), "",Eval(one, 20!value)
N=10 \\ is a multiplier alfa
M=3 \\ is a multiplier  Long
\\ offsets maths
\\ we use a multiplier based on the ratio
\\ value is long (4 bytes)
\\ if we make low as byte and high as byte then we use mult(4,1)
def mult(From1, to1)=From1 div to1
mul=mult(4,2)
Return one, N!Value!M:=0xFF1FA5BB
HEX Eval(one, N!high!mul*M),"",Eval(one, N!low!mul*M), "",Eval(one, N!value!M)
\\ offsets
Print alfa("high"),alfa("low"), alfa("value"), alfa("base1")
\\ real pointer
Print one(0), one(4)-one(3)=len(alfa)
Print Len(one) \\ 14000 bytes



Να κάτι πιο "χοντρό"....
structure alfa {
           structure beta {
                  low as integer
                  high as integer
            }  
            structure delta {
                  low as byte
                  middle1 as byte
                  middle2 as byte
                  high as byte
            }   
            value as long
}
\\  len in bytes, number of offsets
Print len(alfa), len.disp(alfa)
For i=0 to len.disp(alfa)-1
      \\ print names and offsets  and length of offsets
      Print format$("{0:20} {1::4}{2::4}", eval$(alfa, i), eval(alfa), alfa(eval$(alfa, i)))
next i


Structure beta {
      aname as integer *50
      one as alfa*2
}
Buffer Clear alfa1 as alfa, alfa2 as alfa*2


Print len(alfa1), len(alfa2)
Buffer Clear kappa as beta*100
\\ len in bytes (10800), number of parts (100)
Print Len(kappa), Len.disp(kappa)
Return alfa1,0!delta.middle1:=0xFF
Hex eval(alfa1,0!value)
Return alfa1,0!delta.high:=0xFF
Hex eval(alfa1,0!value)
Return alfa2,1:=eval$(alfa1) \\ make a copy
Return kappa, 24!one!1:=eval$(alfa1)
\\ offset here is in bytes
Hex eval(kappa, 24*len(beta)+beta("one")+len(alfa)*1+alfa("beta.high") as integer)
\\ offset here is in item len, 24 for (beta) and for item one 1 for (alfa)
\\ because alfa has 4 bytes automatic return long
Hex eval(kappa,24!one!1)
Return kappa, 24*len(beta)+beta("one")+len(alfa)*1+alfa("delta.middle2") !:=0x33 as byte
Hex eval(kappa,24!one!1)
hex eval(kappa, 24*len(beta)+beta("one")+len(alfa)*1+alfa("delta.middle2") as byte)







Δευτέρα, 16 Μαΐου 2016

Αναθεώρηση 17 νέο αντικείμενο Buffer ή Διάρθρωση

Ετοίμασα νέο αντικείμενο (είχα σκοπό να το κάνω, αλλά τώρα ήταν η ώρα επειδή είχα έτοιμη την υποδομή, δηλαδή το mHandler class το οποίο χειρίζεται αντικείμενα με αναφορά (δεν αντιγράφονται, μόνο η αναφορά αντιγράφεται και αυτή έμμεσα, δηλαδή το mHandler είναι κλάση που δείχνει σε άλλη κλάση. Μέσα σε αυτήν έχουμε το FastCollection.cls που είναι το Inventory ή Κατάσταση (Κατάσταση ειδών) και τώρα το MemBlock.cls που είναι το Buffer ή Διάρθρωση (Διάρθρωση μνήμης).

Η Διάρθρωση ή Buffer είναι μια "έκταση" μνήμης που δύναται να μεγαλώνει ή να μικραίνει και αν δεν πειράζουμε το μέγεθος είναι non movable μνήμη, δηλαδή μνήμη που καταλαμβάνει σταθερό σημείο, η αρχή του δηλαδή είναι σε σταθερό σημείο. Η διάρθρωση είναι χρήσιμη γιατί μπορούμε να σώνουμε πολλά στοιχεία μαζί, με δυαδική μορφή και να τα εξάγουμε σε αρχείο ή να τα εισάγουμε (δουλεύει και με random/τυχαία προσπέλαση) καθώς επίσης για να δίνουμε διευθύνσεις και να καλούμε ρουτίνες (από dll, εξωτερικές βιβλιοθήκες). Θα δούμε παράδειγμα παρακάτω.

Μπορούμε να καταχωρήσουμε ένα Buffer, σε Κατάσταση, σε Πίνακα, σε Ομάδα, σε άλλη μεταβλητή. Μπορούμε να φτιάξουμε συναρτήσεις που να μας γυρνούν ένα Buffer. Μπορούμε να ορίσουμε γενικό ή να χρησιμοποιούμε τοπικό. Η επιστροφή της μνήμης γίνεται αυτόματα ή αν θέλουμε το επιλέγουμε με την εντολή Καθαρό και το όνομα του Buffer.

H Διάρθρωση μοιάζει με τη Κατάσταση, ως προς την χρήση της Επιστροφής (βάζουμε στη Διάρθρωση δεδομένα) και τα Εκφρ() και Εκφρ$() για να πάρουμε αριθμητικά ή αλφαριθμητικά δεδομένα.

Οι αριθμητικές τιμές Byte, Integer, Long είναι χωρίς πρόσημο Unsign (Ψηφίο, Ακέραιος, Μακρύς). Τα μήκη τους είναι 2 Βyte ο Ακέραιος, 4 Byte ο Μακρύς. Έχουμε και τον Double ή Διπλό που πιάνει 8 byte. Μπορούμε να γράφουμε αλφαριθμητικά με δυο byte ο χαρακτήρας ή με ένα.

Ορίζουμε κενή Διάρθρωση ή μη κενή. Κενή σημαίνει ότι θα μηδενιστεί η νέα μνήμη. Αν ορίσουμε ξανά τη Διάρθρωση αλλά δεν αλλάξουμε το μέγεθός της τότε δεν χάνονται τα στοιχεία της. Εσωτερικά χρησιμοποιεί το HeapAlloc.
Στο παράδειγμα παρακάτω καλώ την PathAddBackslashW που βρίσκεται στο Shlwapi.dll με δυο τρόπους. Ο ένας είναι ο παλιός, με χρήση αλφαριθμητικού με αναφορά. Στην ουσία το αλφαριθμητικό δουλεύει σαν Buffer, όμως δεν γνωρίζω που είναι! Δεν με ενδιαφέρει αφού παίρνω το αποτέλεσμα. Αλλά η πληροφορία του Α μου είναι άχρηστη επειδή δεν έχω τον δείκτη που δείχνει τον πρώτο χαρακτήρα του αλφαριθμητικού (τα αλφαριθμητικά αλλάζουν θέσεις πολύ γρήγορα, και την ώρα που το Path$ θα δοθεί στην ρουτίνα, θα γίνει το λεγόμενο copy in copy out, οπότε η διεύθυνση που τελικά θα πάει θα είναι γνωστή "στο βάθος" που δεν δίνει πληροφορίες ο διερμηνευτής.

Με την χρήση του Buffer έχουμε σταθερή θέση μνήμης, και ο δείκτης θα δείχνει το ίδιο στοιχείο (αν δεν αλλάξουμε μέγεθος στο Buffer). Φτιάχνουμε έναν καθαρό Buffer με 250  ακέραιους. Ορίζουμε την  PathAddBackslash2() να δέχεται unsign LPWSTR που είναι ένας δείκτης σε αλφαριθμητικό (αυτό το ρόλο παίζει τώρα ο Buffer). To mypath(0) είναι η διεύθυνση της αρχής του Buffer.To mypath(1) θα είναι 2 Byte πιο μεγάλο, γιατί πάει ανά μέγεθος "διάρθρωσης". Εδώ η Διάρθρωση έχει διάρθρωση σε ακέραιους. Μπορούμε όμως να γράψουμε και ότι άλλο θέλουμε, χρησιμοποιώντας έναν τρόπο cast, παραλλαγής.

\\ we can pass LPWSTR from a string
Declare PathAddBackslash Lib "Shlwapi.PathAddBackslashW" { &Path$ }


P$ = "C:"+String$(Chr$(0), 250)
A= PathAddBackslash( &P$ )
\\ but we can't find the string pointer so we get string until 0
Print LeftPart$(P$, 0)


\\ we get C:\


\\ using buffer we have non movable memory
Declare PathAddBackslash2 Lib "Shlwapi.PathAddBackslashW" { Long LPWSTR }


Buffer Clear mypath As Integer*250
Return mypath, 0 := "C:"
A= PathAddBackslash2(mypath(0))
If A<>0 Then Print eval$(mypath, 0, A-mypath(0))
Clear mypath ' remove buffer



Παρακάτω θα δούμε 4 μικρά προγράμματα με διάρθρωση του Buffer με 1, 2, 4 και 8 byte και σε κάθε περίπτωση θα γράψουμε byte, integer, long, double, Ansi αλφαριθμητικό και UTF-16LE

buffer clear alfa as byte *100
Return alfa, 40 :=-1.232e+45 as double
Return alfa, 30 :=uint(-2000) as long
Return alfa, 28 :=uint(-2001) as integer
Return alfa, 27 :=uint(-21)
Return alfa, 0:="Hello There"  \\22 bytes
Return alfa, 60:=str$("AnsiChars"+chr$(0)) \\ 10 bytes


Print eval(alfa, 40 as double)
Print sint(eval(alfa, 30 as long))
Print sint(eval(alfa, 28 as integer),2)
Print sint(alfa,27)
Print eval$(alfa, 0,22)+"!"
Print chr$(eval$(alfa, 60, 10))


Print "Pointers, can be used with dll"
\\ using item offset (unsing values)
\\ a long in a Declare Lib...is an unsign also
Print alfa(40), alfa(30), alfa(28), alfa(27), alfa(0), alfa(60)
Print alfa(20!)=alfa(20) \\ we can provide byte but here identical


Τώρα με ακέραιο:

buffer clear alfa as integer *100
Return alfa, 40 :=-1.232e+45 as double
Return alfa, 30 :=uint(-2000) as long
Return alfa, 28 :=uint(-2001) as integer
Return alfa, 27 :=uint(-21) as byte
Return alfa, 0:="Hello There"  \\22 bytes
Return alfa, 60:=str$("AnsiChars"+chr$(0)) \\ 10 bytes


\\ using cast we have to multiply by length of item
Print eval(alfa, 40*2 as double)
Print sint(eval(alfa, 30*2 as Long),4)
Print sint(alfa,28)
Print sint(eval(alfa, 27*2 as byte),1)
Print eval$(alfa, 0,22)+"!"
Print chr$(eval$(alfa, 60, 10))


Print "Pointers, can be used with dll"
\\ using item offset (unsing values)
\\ a long in a Declare Lib...is an unsign also
Print alfa(40), alfa(30), alfa(28), alfa(27), alfa(0), alfa(60)
Print alfa(20!)=alfa(10) \\ we can provide byte offset also


Τώρα με Μακρύ
buffer clear alfa as Long *100
\\ we align any value at 4 bytes
Return alfa, 40 :=-1.232e+45 as double
Return alfa, 30 :=uint(-2000) as long
Return alfa, 28 :=uint(-2001) as integer
Return alfa, 27 :=uint(-21) as byte
Return alfa, 0:="Hello There"  \\22 bytes
Return alfa, 60:=str$("AnsiChars"+chr$(0)) \\ 10 bytes


\\ using cast we have to multiply by length of item
Print eval(alfa, 40*4 as double)
Print sint(alfa, 30)
Print sint(eval(alfa, 28*4 as integer),2)
Print sint(eval(alfa, 27*4 as byte),1)
Print eval$(alfa, 0,22)+"!"
Print chr$(eval$(alfa, 60, 10))


Print "Pointers, can be used with dll"
\\ using item offset (unsing values)
\\ a long in a Declare Lib...is an unsign also
Print alfa(40), alfa(30), alfa(28), alfa(27), alfa(0), alfa(60)
Print alfa(20!)=alfa(5) \\ we can provide byte offset also



Τώρα με διπλό (εδώ δεν υπάρχει είδος unsign)

buffer clear alfa as double *100
\\ using align to 8 bytes
Return alfa, 40 :=-1.232e+45 as double
Return alfa, 30 :=uint(-2000) as long
Return alfa, 28 :=uint(-2001) as integer
Return alfa, 27 :=uint(-21) as byte
Return alfa, 0:="Hello There"  \\22 bytes
Return alfa, 60:=str$("AnsiChars"+chr$(0)) \\ 10 bytes


\\ using cast we have to multiply by length of item
Print eval(alfa, 40)
Print sint(eval(alfa, 30*8 as Long),4)
Print sint(eval(alfa, 28*8 as Long),2)
Print sint(eval(alfa, 27*8 as byte),1)
Print eval$(alfa, 0,22)+"!"
Print chr$(eval$(alfa, 60, 10))


Print "Pointers, can be used with dll"
\\ using item offset (unsing values)
\\ a long in a Declare Lib...is an unsign also
Print alfa(40), alfa(30), alfa(28), alfa(27), alfa(0), alfa(60)
Print alfa(24!)=alfa(6) \\ we can provide byte offset also



Και εδώ χρησιμοποιούμε την Structure (με αλφαριθμητικό κάνει κάτι άλλο, δίνει τη δομή μιας βάσης δεδομένων, αλλά εδώ την χρησιμοποιώ με απλό αναγνωριστικό)
Τώρα η Διάρθρωση έχει στρωμένες Δομές διαφόρων μεγεθών!

structure stringA {
      str as integer*20
}
structure beta {
      Alfa as byte
      align1 as byte*3
      Kappa as long*20
      Name as stringA*10
      kappa2 as double
}
Buffer clear alfa as beta*100
Print Len(alfa), len.disp(alfa)
Print 5*len(beta)+ beta("kappa"), beta("kappa")
Return alfa, 50!kappa!10:=50
\\ sLong=4
\\ Return alfa, 5*len(beta)+ beta("kappa")+10*sLong! :=50 as long
\\ Print eval(alfa, 5*len(beta)+ beta("kappa")+40  as long)


Print eval(alfa, 50!kappa!10)
return alfa, 50!name!5:="George", 50!name!6:="Hello"
Print eval$(alfa, 50!name!5, 20)
Print eval$(alfa, 50!name!6, 20)
Print Len(alfa), type$(beta)
Print len(beta), len.disp(beta)
Print beta("Alfa"), beta("Name")




Εισαγωγή από αρχείο (το φτιάχνουμε κιόλας)

dir user
document A$={hello there
            Something Else
            third line
            }
Save.Doc A$, "here.txt", 0 ' Utf-16
   
If exist("here.txt") then {
      ALFA.items=filelen("here.txt")
      buffer clear ALFA as integer * ALFA.items
      f=1
      open "here.txt" for random as f Len=Len(ALFA) \\Len(ALFA) bytes
      get #f, ALFA
      close #f
} else print "can't find" : exit
Report eval$(ALFA)


Με άλλο τρόπο:

dir user
document A$={hello there
            Something Else
            third line
            }
Save.Doc A$, "here.txt", 0 ' Utf-16
   
If exist("here.txt") then {
      ALFA.items=filelen("here.txt")/2
      buffer clear ALFA as integer * ALFA.items
      open "here.txt" for wide input as f
      Return Alfa, 0:=Input$(#f, ALFA.items)
      close #f
} else print "can't find" : exit
Report eval$(ALFA)
document B$=eval$(ALFA)
Report B$



Εισαγωγή από ANSI αρχείο

dir user
document A$={hello there
            second line
            }
Save.Doc A$, "here.txt", 3 ' Ansi


If exist("here.txt") then {
      buffer alfa as byte * filelen("here.txt")
      f=1
      open "here.txt" for random as f Len=Len(alfa) \\Len=filelen("here.txt")
      get #f, alfa
      close #f
} else print "can't find"


Report chr$(eval$(alfa))


Βάζουμε σε ένα "δυαδικό" αρχείο, εγγραφές κάθε 50 χαρακτήρες (100 bytes). έχουμε ένα Buffer για 50 εγγραφές και ένα για μια. Μαζί με το όνομα βάζουμε σε 40 byte μετά την αρχή (στο 0 είναι το πρώτο Byte) έναν Διπλό (θέλει 8 bytes). Δεν έχουμε δηλώσει ότι θα βάζουμε διπλούς, αλλά χρησιμοποιούμε την θέση σε Bytes με το ! αμέσως μετά τον αριθμό και το as double.

structure record1 {
      all as integer*50
}
Buffer Clear alfa as record1*50
Buffer clear beta as record1
Return alfa, 30:="George", 3040 ! := -343.434 as double
Open "M1.klm" for wide random as f Len=50
      Put #f, eval$(alfa,30),1
      Get #f, beta(0),1
close f
Print eval$(beta, 0,12), eval(beta, 40 as double)


με μια μικρή παραλλαγή βάζουμε το George σε 6 bytes (ANSI μορφή)

structure record1 {
      all as integer*50
}
Buffer Clear alfa as record1*50
Buffer clear beta as record1
Return alfa, 30:=str$("George"), 3040 ! := -343.434 as double
Open "M134.klm" for wide random as f Len=50
      Put #f, eval$(alfa,30),1
      Get #f, beta(0),1
close f
Print chr$(eval$(beta, 0,6)), eval(beta, 40 as double)