Τρίτη 24 Φεβρουαρίου 2026

Revision 7 Version 14

 I found a problem when I use the ip 142.250.187.100 for test but now not responding. This affect then  read only variables named INTERNET and INTERNET$. Revision 7 fix the problem.


We can bypass INTERNET functionality by using a global group INTERNET which return a value (so call a function without using parenthesis postfix on the name):

global Internet=lambda (a$="100")-> {
group a {
ip$="142.250.187."+a$
value {
declare client client
With client, "NoError", True
Method client, "SetTimeouts", 100, 300, 200, 300
Method client, "connect", .ip$, 80 As connect
=connect
}
}
=group(a)
}() // ("10") is ok
module inner {
? internet 'our global  using 142.250.187.100 which not working
? @internet ' original  - version 14 revision 7 return true, all other return false
}
Inner


The Inner module can call original internet using @. Global Grouo Internet { } not bypass then original Internet read only value, but using a simple global variable which get the group by a function do the job.


This module show how we can use a group to do the same job as internet$ do.

module testB {
global internet$="" ' need this to bypass the original internet$
function mockup(a$="100") {
group z$ {
ip$="142.250.187."+a$
value {
=" 0"
declare client client
With client, "NoError", True
Method client, "SetTimeouts", 100, 300, 200, 300
Method client, "connect", .ip$, 80 As connect
if connect then
declare HttpsRequest HTTPS.REQUEST
With HttpsRequest,"BodyFistLine" As RESP$
Method HttpsRequest, "HttpsRequest", "HTTPS://ifconfig.co/ip" As OK
if OK then =RESP$
end if
}
}
     =group(Z$)
}
global internet=mockup()
? internet$
let internet=mockup("10")
? internet$
list
}
testB
? internet$ ' this is the original



Πέμπτη 5 Φεβρουαρίου 2026

Ternary Logic

https://rosettacode.org/wiki/Ternary_logic#M2000_Interpreter 

Task
  • Define a new type that emulates ternary logic by storing data trits.
  • Given all the binary logic operators of the original programming language, reimplement these operators for the new Ternary logic type trit.
  • Generate a sampling of results using trit variables.


module Ternary_logic {
class trit {
private:
variant val
function copy() {
m=this
m.trit
=m
}
public:
enum ternary {
True="True"
Maybe="Maybe"
False="False"
}
function true() {
=.copy(.True)
}
function maybe() {
=.copy(.Maybe)
}
function false() {
=.copy(.False)
}
operator "==" (k as trit) {
push .val=k.val
}
operator "^^" (k as trit) {
select enum .val
case .True
.val<=k.val
case .False
.val<=.False
case else
if k.val=.False then .val<=.False else .val<=.Maybe
end select
}
operator "^|" (k as trit) {
select enum .val
case .True
.val<=k.val
case .False
.val<=.True
case else
if k.val=.True then .val<=.True else .val<=.Maybe
end select
}
operator "||" (k as trit) {
select enum .val
case .False
.val<=k.val
case .True
.val<=.True
case else
if k.val=.True then .val<=.True else .val<=.Maybe
end select
}
operator "~~" (k as trit) {
select enum .val
case .True
.val<=k.val
case .False
if k.val=.True then .val<=.False else.if k.val=.False then .val<=.True else .val<=k.val
case else
.val<=.Maybe
end select
}
operator unary {
select enum .val
case .True
.val<=.False
case .False
.val<=.True
end select
}
group value {
value {
link parent val to val
=val
}
}
module trit {
if empty or not isnum then
read s as .ternary=.Maybe
.val<=s
else.if isnum then
read what
if what then
.val<=.True
else
.val<=.False
end if
end if
}
}
function enum2array(t) {
m=each(t)
while m {data eval(m)}
=array([])
}
string out, nl={
}
q=trit()
m=trit()
k=enum2array(q.ternary)
out ="not a" + nl
a=each(k)
while a
q=trit(array(a))
z=-q
out +="    ternary_not "+(q.value) + " = " + (z.value) + nl
end while
out +="a and b" + nl
a=each(k)
while a
b=each(k)
while b
q=trit(array(a))
m=trit(array(b))
z=q ^^ m
out += "    " + (q.value) + " ternary_and " + (m.value) + " = " + (z.value) + nl
end while
end while
out +="a or b" + nl
a=each(k)
while a
b=each(k)
while b
q=trit(array(a))
m=trit(array(b))
z=q || m
out += "    " + (q.value) + " ternary_or " + (m.value) + " = " + (z.value) + nl
end while
end while
out +="if a then b" + nl
a=each(k)
while a
b=each(k)
while b
q=trit(array(a))
m=trit(array(b))
z=q ^| m
out += "    if " + (q.value) + " then " + (m.value) + " = " + (z.value) + nl
end while
end while
out +="a is equivalent to b" + nl
a=each(k)
while a
b=each(k)
while b
q=trit(array(a))
m=trit(array(b))
z=q ~~ m
out += "    "+(q.value) + " is equivalent to " + (m.value) + " = " + (z.value) + nl
end while
end while
report out
clipboard out
}
Ternary_logic

Output:

not a

    ternary_not True = False

    ternary_not Maybe = Maybe

    ternary_not False = True

a and b

    True ternary_and True = True

    True ternary_and Maybe = Maybe

    True ternary_and False = False

    Maybe ternary_and True = Maybe

    Maybe ternary_and Maybe = Maybe

    Maybe ternary_and False = False

    False ternary_and True = False

    False ternary_and Maybe = False

    False ternary_and False = False

a or b

    True ternary_or True = True

    True ternary_or Maybe = True

    True ternary_or False = True

    Maybe ternary_or True = True

    Maybe ternary_or Maybe = Maybe

    Maybe ternary_or False = Maybe

    False ternary_or True = True

    False ternary_or Maybe = Maybe

    False ternary_or False = False

if a then b

    if True then True = True

    if True then Maybe = Maybe

    if True then False = False

    if Maybe then True = True

    if Maybe then Maybe = Maybe

    if Maybe then False = Maybe

    if False then True = True

    if False then Maybe = True

    if False then False = True

a is equivalent to b

    True is equivalent to True = True

    True is equivalent to Maybe = Maybe

    True is equivalent to False = False

    Maybe is equivalent to True = Maybe

    Maybe is equivalent to Maybe = Maybe

    Maybe is equivalent to False = Maybe

    False is equivalent to True = False

    False is equivalent to Maybe = Maybe

    False is equivalent to False = True



Τετάρτη 21 Ιανουαρίου 2026

Array Length

https://rosettacode.org/wiki/Array_length#M2000_Interprete

Update using the three different objects for array: tuple, mArray, RefArray. Old versions of M2000 had only mArray, always as Variant (and tuple was mArray), then a RefArray used with squared brackets but using various item size from Byte, then mArray also drive a RefArray (using dimensions over RefArray - which the second object is hidden) so we may have A(10,2) as Byte, then tuple are an object always flat and for variants only. The B()=A when A point to tuple copies to B() as mArray. An mArray is heavier object, because holds array for one to ten dimensions of values for low and high bound, p;us an array for each dimension for offsets on a flat array (1d)  for item values (or pointer to RefArray, if we use specific type). A pointer to array may point to tuple or mArray. A pointer to RefArray can't change to anything else (can be value for a variant type variable or array item). A RefArray may have one or two dimensions, but we can construct any number by using a variant type RefArray using each item a refArray and so on. So we can make jagged arrays, using RefArrays. We can do the same using mArray of type variant or tuple. So A=((,), (1,), (1,2), (1,2,3)) is a jagged array of type tuple. The (,) is an empty tuple (length zero). For mArray arrays the A()=B() is a copy of B() to A(). and A()=A when A is pointer to mArray or tuple is also a copy, but A=B() is a pointer to B() copied to A.

' A is a pointer to array
A=("Apple", "Orange")
Print  Len(A)=2 ' True
Print Type$(A)="tuple"
Dim A(10,2) as Byte=32, B(), C(10,2)
Print Len(A())=20, Len(B())=0, Len(C())=20
C(1,1)="String"
C(2,0)=100, 3
Dim C(6,2)
Print Len(C())=12
Print C(2,0)=100, C(2,1)=3, C(1,1)="String"
B()=A ' this is a copy
A=(,)
Print B(1)="Orange", Len(B())=2, Len(A)=0
Print Type$(B())="mArray", Type$(A)="tuple"
A=B() ' now A point to B()
Print Type$(A)="mArray"
Print A(5,1)=32, Type$(A(5,1))="Byte"
A=A() ' point to A() we address through A as a flat 1d array.
Print Type$(A,0)="Byte", A#val(0)=32, A#sum()=32*20, A()#sum()=32*20
Print Dimension(A())=2 ' Number of dimensions
Print Dimension(A(), 1)=10, Dimension(A(), 2)=2 ' length for each dimension
Print Dimension(A(), 1, 0)=0, Dimension(A(), 1, 1)=9 ' lower and upper bound on 1st Dimension


' RefArray type of Arrays (always 0 lower bound)
Variant D[100]
Print Len(D)=101, Type$(D)="RefArray"
Byte F[9][1]
Print Len(F)=10
F[3][0]=111
F[3][4]=105
D[3]=F[3][] ' copy a row from F[3] to D[3]
F[3][0]++

Print D[3][0]=111, D[3][4]=105, F[3][0]=112 

Παρασκευή 26 Δεκεμβρίου 2025

Nested Functions

The M2000 programming language has two types of inner (nested) functions (and some variations for the first type).

Normal Function: 

- We can nest normal functions:

Here we have two functions beta(), but only one inner (nested) in afa() function. A normal function may have modules and functions. Each inner function has own scope, so we can't call something out of block of code of function (except the sane name, as a recursion call)

function beta(y) {
=y/2
}
function alfa(x) {
function beta(y) {
=y*100
}
=x*beta(x)
}
print alfa(20)=40000
print beta(40000)=20000


- Using lambda functions:

A lambda function is an object which combines a list of variables and code for normal function. We can place a lambda function as variable in the list of variables of lambda. The first part of the next example has alfa function which define a nested function beta(). The second part, has a alfa function which have a closure beta which is lambda function, so this definition of alfa has beta all the time (but the first definition has beta at the moment we defined it - so we have some space before which beta not exist).

beta=lambda (y)-> {
=y/2
}
alfa=lambda (x) ->{
function beta(y) {
=y*100
}
=x*beta(x)
}
print alfa(20)=40000
print beta(40000)=20000


beta=lambda (y)-> {
=y/2
}
alfa=lambda (y)->y*100
alfa=lambda beta=alfa (x) ->{
=x*beta(x)
}
print alfa(20)=40000
print beta(40000)=20000


The alfa lambda function can be written like these too:

alfa=lambda
beta=lambda (y)
->{
=y*100
}
(x)
->{
=x*beta(x)
}
print alfa(20)=40000


alfa=lambda
beta=lambda (y)->y*100
(x)
->{
=x*beta(x)
}
print alfa(20)=40000

alfa=lambda
beta=lambda (y)->{
=y*100
}
(x) ->{
=x*beta(x)
}


alfa=lambda
beta=lambda (y) ->y*100

(x) ->{
=x*beta(x)
}


- We can use normal functions as object functions, so we can use siblings functions (as objects functions) as well as inner functions. Using object we gain a state part which we can study or display later:

group delta {
long counter
function beta(y) {
// increment this.counter
.counter++
=y*10
}
function alfa(x) {
function beta(y) {
=y*100
}
=x*.beta(x)+beta(x/2)
}
function alfa2(x) {
function beta(y) {
=y*500
}
// call this.beta() and local beta()
=x*.beta(x)+beta(x/2)
}
}
print delta.beta(10)=100
print delta.alfa(20)=5000
print delta.alfa2(20)=9000
print delta.counter=3


We can't call delta.alfa2().beta() becauses beta() not exist at the level of delta. When we call the delta.alfa2() then the Function statement define the function (writing in a list of functions), and at the exit from delta.alfa2() the inner function deleted. From delta.alfa2() we can call the delta.beta() because it is a member of delta, but without using the delta (because this is something not known for the context of delta.alfa2()). We use the THIS object as this.beta() or .beta() which is the same.

We can use Class statement to make one or more objects:

class delta {
long counter
function beta(y) {
// increment this.counter
.counter++
=y*10
}
function alfa(x) {
function beta(y) {
=y*100
}
=x*.beta(x)+beta(x/2)
}
function alfa2(x) {
function beta(y) {
=y*500
}
// call this.beta() and local beta()
=x*.beta(x)+beta(x/2)
}
}
delta=delta()
print delta.beta(10)=100
print delta.alfa(20)=5000
print delta.alfa2(20)=9000
print delta.counter=3

We can make pointers to objects too. An object created as a pointer to object deleted after last pointer deleted, but an object like delta which created as "named object" or group, deleted same time as all variabled deleted in a module or function, at the end of call, or in the For object { } block at the exit of block. See the => used instead of dot for addressing a member through a pointer of object (group).

// same as delta=pointer(delta())
delta1->delta()
print delta1=>beta(10)=100
print delta1=>alfa(20)=5000
print delta1=>alfa2(20)=9000
print delta1=>counter=3


Simple Function:

This example is like the one with an object (group delta), but now we use simple functions. We can define functions in a simple function using @ (which call always the normal function constructor). Without the @ workaround the search for a simple function return error. Simple functions are not listed in the list of functions, but instead first time searched from the end and then listed as part of code of current scope. A simple function has no own scope (use the caller scope, so in the example counter is on scope in any simple function) but anything new defined (using Local statement to hide a previous defined identifier) would erased at the end of call. Also a simple function use same stack of values from the caller (this speed up the call). The function new variation make a new function always (without new the new definition replace an old one if the name exist).

long counter


print @beta(10)=100
print @alfa(20)=5000
print @alfa2(20)=9000
print counter=3


function beta(y)
counter++
=y*10
end function
function alfa(x)
@function new beta(y) {
=y*100
}
=x*@beta(x)+beta(x/2)
end function
function alfa2(x)
@function new beta(y) {
=y*500
}
=x*@beta(x)+beta(x/2)
end function



Τρίτη 16 Δεκεμβρίου 2025

Search Inkscape.exe (or any exe file) Updated

This is the new solution using ItemUrl which return file://path using / as URL. Also there is a way to read the path from link. Path from link maybe is ANSI encoded for language of OS system (we have to use the proper locale id (using Locale 1032 for greek names), or UTF16LE. So if you make a text file والكليات.txt in Documents and make a lnk file and then move this file to Desktop folder (and rename the link as والكليات ) then the inner function findlnk find the unicode name. All names have 0 at the end (like all C strings).

When we load the lnk file in a buffer we get offsets from base 0 (not base 1 for file). 


function findExefile(this$, typ$="exe") {
function findlnk(linkfile$) {
function ReadSingleString(b as buffer, offset) {
=leftpart$(chr$(eval$(b, offset, len(b)-offset)), chr$(0))
}
function ReadDoubleString(b as buffer, offset) {
=leftpart$(eval$(b, offset, len(b)-offset), chr$(0))
}
=""
let a=buffer(linkfile$), offset=0
if eval(a, offset as long)=76 then
ShortcutFilename=""
Long offset=20
long Flags=a[offset]
if binary.and(Flags,2)>1 then
offset= eval(a, 76 as integer)+76+2
end if
if binary.and(Flags,2)>0 then
TotalStructLength=eval(a, offset as long)
PtrBasePath=eval(a, offset+16 as long)
PtrNetworkVolumeInfo=eval(a, offset+20 as long)
PtrFilename=eval(a, offset+24 as long)
If PtrBasePath Then
ShortcutFilename = ReadSingleString(a, offset + PtrBasePath)
if len(ShortcutFilename)<TotalStructLength-PtrBasePath-2 then
ShortcutFilename=ReadDoubleString(a, offset + PtrBasePath+len(ShortcutFilename)+1)
end if
Else.If PtrNetworkVolumeInfo Then
ShortcutFilename = ReadSingleString(a, offset + PtrNetworkVolumeInfo + &H14)
End If
If PtrFilename Then
Str = ReadSingleString(a, offset + PtrFilename)
If Str <> "" and false Then
If Right$(ShortcutFilename, 1) <> "\" Then
ShortcutFilename += "\"
End If
ShortcutFilename += Str
End If
End If
= ShortcutFilename
end if
end if
}
ShortcutFilename=""
declare cn "ADODB.Connection"
method cn, "open", "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
search$={SELECT System.ItemUrl
FROM SystemIndex
WHERE System.FileName = '@@@.###'
}
if typ$="" else
method cn, "execute", replace$("@@@", this$, replace$("###",typ$,search$)) as rs
with rs, "eof" as rs.EOF, "fields" as field()
while not rs.EOF {
ShortcutFilename=field(0)
method rs, "movenext"
}
end if
if ShortcutFilename="" else =replace$("/","\",rightpart$(ShortcutFilename, ":")): exit
method cn, "execute", replace$("@@@", this$, replace$("###","lnk",search$)) as rs
if Valid(rs.EOF) Else with rs, "eof" as rs.EOF, "fields" as field()
while not rs.EOF {
ShortcutFilename=field(0)
method rs, "movenext"
}
if ShortcutFilename="" then exit
=findlnk(replace$("/","\",rightpart$(ShortcutFilename, ":")))


}
Print findExefile("Inkscape")
Print findExefile("والكليات", "txt")

This is from my example, there are two links in desktop folder which the Search.CollatorDSO provider can find (We can change the folder list from windows indexing options)

C:\Program Files\Inkscape\bin\inkscape.exe  (was ANSI)

C:\Users\fotod\OneDrive\Έγγραφα\والكليات.txt (was two parts: the ansi but with three languages English, Greek, Arabian, was impossible to read, and the UTF16LE which is ok)


OLD: works if we have English language for OS, because the ItemPathDisplay return names like folder USERS using the system language of Windows. 

Using simple function:

Print @findExe("Inkscape")


function findExe(this$)
declare local cn "ADODB.Connection"
method cn, "open", "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
local search$={SELECT System.ItemPathDisplay
FROM SystemIndex
WHERE System.FileName = '@@@.exe'
}
method cn, "execute", replace$("@@@", this$, search$) as new rs
with rs, "eof" as new rs.EOF, "fields" as new field()
=""
if not rs.EOF then =field(0)
end function


Using function (can be member of a class too)

function findExe(this$) {
declare cn "ADODB.Connection"
method cn, "open", "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
search$={SELECT System.ItemPathDisplay
FROM SystemIndex
WHERE System.FileName = '@@@.exe'
}
method cn, "execute", replace$("@@@", this$, search$) as rs
with rs, "eof" as rs.EOF, "fields" as field()
=""
if not rs.EOF then =field(0)
}
Print findExe("Inkscape")


This is the simple version which find inkscape exe and lnk files:

declare cn "ADODB.Connection"
method cn, "open", "Provider=Search.CollatorDSO;Extended Properties='Application=Windows';"
search$={SELECT System.ItemNameDisplay, System.ItemPathDisplay
FROM SystemIndex
WHERE System.FileName = 'Inkscape.exe'
OR (System.FileExtension = '.lnk' AND System.ItemNameDisplay LIKE '%Inkscape%')
}
method cn, "execute", search$ as rs
with rs, "eof" as rs.EOF, "fields" as field()
while not rs.EOF
Print field(0);" -> ";field(1)
method rs, "MoveNext"
end while


Κυριακή 29 Ιουνίου 2025

PRINT USING

 A function which convert BASIC Print Using to M2000 format$()


FUNCTION USING(A$) {
VAR P,B$,B1$,C$, ALL=STACK.SIZE
REPEAT
B1$=LEFTPART$(A$,"#")+"{"+P
B$+=B1$
A$=MID$(A$,LEN(B1$))
INTEGER D2=0, D1=0, I=1
WHILE MID$(A$,I,1)="#"
I++
END WHILE
D2=I
IF MID$(A$,I,1)="." THEN
D2++:I++
D1=0
WHILE MID$(A$,I,1)="#"
D1++
I++
END WHILE
D2+=D1
END IF
A$=MID$(A$,D2)
C$=LEFTPART$(A$,"#")
IF LEN(C$)=0 THEN C$=A$:A$=""
B$+=":"+D1+":-"+D2+"}"
P++
WHEN A$<>""
TRY OK {
INLINE "=FORMAT$("+QUOTE$(B$+C$)+STRING$(", NUMBER", P)+")"
}
IF ERROR OR NOT OK THEN C$=ERROR$: ERROR "Problem on parameter #"+(ALL-STACK.SIZE+1)
}
PRINT USING("A=#####.##ms B=##.##sec c=###%", 1, 1.3, 12.4)
PRINT FORMAT$("A={0:2:-8}ms B={1:2:-5}sec c={2:0:-3}%",1, 1.3, 12.4)