Programming language M2000 introduce a special Stack which can be used as queue (FIFO) and stack (LIFO), and is an object. This Stack is different from a return stack.
When we call a function or a module, a new runnable object start. Each runnable object has a pointer names Soros to stack of values. When a module called then this pointer point to parent Stack. Functions when called from expression get a new stack object.
In every line of code we can do that
What happen?
Statement Push insert first value 10 to top of stack, then value 2 to top of stack. Next statement use Number which get a number from stack, so take 2, and the second Number take 10 so Print prints 20.
Push works for every type of expression
A Push A : Read B can be as Let B=A (it isn't the same as B=A, because in simple assignment interpreter know about B before find A, and in Let, first find A and then B. In a Let A(10+2)=B(3) expression 10+2 executed after reading B(3). In a A(10+2)=B(3) expression 10+2 executed before reading B(3)
Flush
Print Empty \\ True
So now we insert three values to stack, but without using Push, which put to top of stack. We use Data which put items in bottom of stack (append to stack). This code is similar to Basic (although For isn't work like Basic at 100%- except when we use an internal software switch +For).
So we start with an empty stack, and we append 100, then "Hello", then 30, then 200, then "There", then 40. In a For loop we read 2 times 3 values. We have six items in stack, and the types are the proper types.
Because Subs and Modules have same stack as parent we can do that:
Example with Sub (subs have same name space as modules which call them). In PrintAll() there is Many as parameter. Internal interpreter execute a Read New Many. If a sub call it's name and has a parameter, the Read New make space for new variable, and the new variable hide old one, until sub end (modules aren't recursive with standard call, functions are always recursive)
Print Stack.Size \\ return size of current stack
Print Empty \\-1 if Stack.Size=0
Print IsNum \\ -1 if top is number, no error if we have empty stack.
Print IsLet \\ Is letter -1 if top is string, no error if we have empty stack.
Print Envelope$() \\ return a string with a letter for each item in stack.
Print Match("NN") \\ return -1 if top two values are numbers
Stack \\ without parameters display stack
Over 2 \\ make a copy of 2nd element to top
Over 2,4\\ make a copy of 2nd, four times
Shift 3 \\ move 3d element as 1st
Shift 3,2 \\ move 3d element as 1st two times
ShiftBack 3 \\ move first element to 3d position
ShiftBack 3,2 \\ move first element to 3d position, two times
Flush
Data 1,2,3,4,5,6
Over 2,4
Stack \\ 1 2 1 2 1 2 3 4 5 6
Shift 2 \\ 2 1 1 2 1 2 3 4 5 6
Shift 2, 4 \\ 1 1 2 1 2 2 3 4 5 6
ShiftBack 2, 4 \\ 2 1 1 2 1 2 3 4 5 6
ShiftBack 2 \\ 1 2 1 2 1 2 3 4 5 6
Drop 4 \\ 1 2 3 4 5 6
Push 0 \\ 0 1 2 3 4 5 6
Data 7 \\ 0 1 2 3 4 5 6 7
Stack \\ display stack
0 1 2 3 4 5 6 7
Print Envelope$() \\ N for Number
NNNNNNNN
Print Match("NNN") \\ -1
Print Match("NSS") \\ 0 (S for String) use Help Match() to find all letters.
Print StackType$(2)
Print StackItem(2)
1
Print StackType$(2)
Number
Stack A { Push 15, 25 : ShiftBack 4 : ShiftBack 2}
Print A \\ 10 15 20 25 30
Stack A {ShiftBack 1, -Stack.Size } \\ reverse order
Print A \\ 30 25 20 15 10
Stack A {ShiftBack 1, -Stack.Size } \\ reverse order
Print A \\ 10 15 20 25 30
\\ there is a symmetry with Shift if we use -Stack.Size
Stack A {Shift 1, -Stack.Size }
Print A \\ 30 25 20 15 10
Stack A {Shift 1, -Stack.Size }
Print A \\ 10 15 20 25 30
Stack A { Over Stack.Size, Stack.size} \\ double stack
Print A \\ 10 15 20 25 30 10 15 20 25 30
Clear \\ clear all variables - stack objects without a pointer to point to them erased
B= Stack:="L","O"
Def Disp$(a$, b$, c$, d$, e$)=a$+b$+c$+d$+e$
Print Disp$(!A, !B), Len(A), Len(B) \\ Hello 0 0
Print Disp$("Y","E","S","","") \\ YES
Print Disp$(!stack:="Y","E","S","","") \\ YES
Print Disp$(!(stack:="Y","E"),"S","","") \\ YES
Print Disp$(!(stack:="Y","E"),!stack:="S","","") \\ YES
Print Disp$(!(stack:="Y","E"),!(stack:="S","","")) \\ YES
A=Stack:="H","E","L"
B= Stack:="L","O"
A=Stack:=!A, !B
Print A \\ H E L L O
Print Len(A), Len(B) \\ 5 0
B=Stack(A, A) \\ merge two copies of A as a new object for B
Print B \\ H E L L O H E L L O
Print Len(A), Len(B) \\ 5 10
Flush
Group Alpha {
x=10
}
K=Stack:=100, Alpha
For This {
Local N
N=StackItem(K, 2)
N.x++
Return K, 2:=N
}
Stack K \\ place K to current stack
Read Z, M
Print Z, M.x \\ 100, 11
A (,) is an empty "auto" array, a (1,) is an array with one item, a ((1,2),(3,4)) is an array with two items as arrays of two items. We can make arrays using Dim, or by using auto arrays. We can link a pointer A (pointer to array) to a name like A() to get another interface. An array used by pointer has different interface. If A=(1,2,3) then A+=10 make A as 11,12,13. In any interface we can't use arrays to perform mat operations (maybe in another version of M2000).
here we make three kind of objects, mStiva (Stack Object), mArray and Inventory. In key "100" in inventory B we put a shallow copy of A(), where reference types like stack object, just copied pointer to them. (Groups are not reference types, so they copied as values, but inside groups, members with reference types copied by pointer).
Arrays with parenthesis copied as values (shallow copy), but Arrays as pointers copied as reference type. X=Y() means X is a pointer to Y(), Z()=Y() means Z() is a shallow copy of Y(). Link X to B() means that we get two interfaces, and B() is reference to X, so if any change means the other change too, because is only one pointer, and two reference to that pointer. X=Y() and Z=Y() are two identifiers which point to Y(), so X=(1,2,3,4) make X to leave pointer to Y() and point to a new auto array.
100 Print "ok"
110 Return
When we call a function or a module, a new runnable object start. Each runnable object has a pointer names Soros to stack of values. When a module called then this pointer point to parent Stack. Functions when called from expression get a new stack object.
In every line of code we can do that
Push 10, 2 : Print Number*Numberand we can print 20
What happen?
Statement Push insert first value 10 to top of stack, then value 2 to top of stack. Next statement use Number which get a number from stack, so take 2, and the second Number take 10 so Print prints 20.
A=10Before read value of B is in top of stack, or position 1, and value of A is in position 2. (there is a Swap A, B swapping values without using stack).
B=5
Push A, B : Read A, B
Print A, B
5 10
Push list of items push each value to top of stack.So using Push and Read we use Stack as LIFO (Last input First Output)
Read list of variables, where reads for every variable from top of stack.
Push works for every type of expression
Expressions in M2000 can return numbers, strings and objects too.
An example with an array pointer in current stack
Lets initialize A as an array of 4 items. Variable A is a pointer to Array. Isn't a memory pointer (real pointers are hidden in M2000, except for Buffers). We use current stack (we didn't see any name but Push pushing to current stack, and Read reading from current stack)A=(1,2,3,4)
Print A \\ print 1 2 3 4
Push A : Read B
Print B \\ print 1 2 3 4
A+=10
Print A \\ 11 12 13 14
Print B \\ 11 12 13 14
A Push A : Read B can be as Let B=A (it isn't the same as B=A, because in simple assignment interpreter know about B before find A, and in Let, first find A and then B. In a Let A(10+2)=B(3) expression 10+2 executed after reading B(3). In a A(10+2)=B(3) expression 10+2 executed before reading B(3)
Using Current Stack as Queue.
First let empty the stack, with Flush command and check if it is empty with Empty as read only variable.Flush
Print Empty \\ True
So now we insert three values to stack, but without using Push, which put to top of stack. We use Data which put items in bottom of stack (append to stack). This code is similar to Basic (although For isn't work like Basic at 100%- except when we use an internal software switch +For).
Data 100, "Hello", 30, 200
Data "There", 40
For i=1 to 2
Read N, A$, BNext i
Print N, A$, B
So we start with an empty stack, and we append 100, then "Hello", then 30, then 200, then "There", then 40. In a For loop we read 2 times 3 values. We have six items in stack, and the types are the proper types.
Because Subs and Modules have same stack as parent we can do that:
Module FeedMe {
Flush}
Data 100, "Hello", 30, 200
Data "There", 40
FeedMe
For i=1 to 2
Read N, A$, BNext i
Print N, A$, B
Example with Sub (subs have same name space as modules which call them). In PrintAll() there is Many as parameter. Internal interpreter execute a Read New Many. If a sub call it's name and has a parameter, the Read New make space for new variable, and the new variable hide old one, until sub end (modules aren't recursive with standard call, functions are always recursive)
FeedMe()
PrintAll(2)
Sub PrintAll(Many)
For i=1 to ManyEnd Sub
Read N, A$, BNext i
Print N, A$, B
Sub FeedMe()
FlushEnd Sub
Data 100, "Hello", 30, 200
Data "There", 40
Compute/Analyze Stack.
Print Stack.Size \\ return size of current stack
Print Empty \\-1 if Stack.Size=0
Print IsNum \\ -1 if top is number, no error if we have empty stack.
Print IsLet \\ Is letter -1 if top is string, no error if we have empty stack.
Print Envelope$() \\ return a string with a letter for each item in stack.
Print Match("NN") \\ return -1 if top two values are numbers
Stack \\ without parameters display stack
Passing arguments in Function
In a function stack is always new at the beginning. And at the exit stack destroyed. Interpreter call a function without knowing about how many parameters and the types need for calling it. Anything we give as arguments in a function/module call, passed to stack (fresh for function, same for module/sub)Flush
Push 1
Function GetAll {
Stack
}
Print GetAll(1,2,3,4,5) \\ show stack 1 2 3 4 5
Stack \\ show stack 1
Stack Operations
Drop 10 \\ remove top 10 items (if stack.size<10 then we get an error).Over 2 \\ make a copy of 2nd element to top
Over 2,4\\ make a copy of 2nd, four times
Shift 3 \\ move 3d element as 1st
Shift 3,2 \\ move 3d element as 1st two times
ShiftBack 3 \\ move first element to 3d position
ShiftBack 3,2 \\ move first element to 3d position, two times
Flush
Data 1,2,3,4,5,6
Over 2,4
Stack \\ 1 2 1 2 1 2 3 4 5 6
First Over 2 make 2123456
Second Over 2 make 12123456
Third Over 2 make 212123456
Fourth Over 2 make 1212123456
Shift 2 \\ 2 1 1 2 1 2 3 4 5 6
Shift 2, 4 \\ 1 1 2 1 2 2 3 4 5 6
ShiftBack 2, 4 \\ 2 1 1 2 1 2 3 4 5 6
ShiftBack 2 \\ 1 2 1 2 1 2 3 4 5 6
Drop 4 \\ 1 2 3 4 5 6
Push 0 \\ 0 1 2 3 4 5 6
Data 7 \\ 0 1 2 3 4 5 6 7
Stack \\ display stack
0 1 2 3 4 5 6 7
Print Envelope$() \\ N for Number
NNNNNNNN
Print Match("NNN") \\ -1
Print Match("NSS") \\ 0 (S for String) use Help Match() to find all letters.
Print StackType$(2)
Stack Access Items by index
Top item is in index 1. Using StackItem() we read an item without pop it from stack.Print StackItem(2)
1
Print StackType$(2)
Number
Stack Objects
we can use [] to get current stack and place an empty stack in as current stack. Statement A=[] set a pointer to stack object in A, and set to Soros in runnable object a new stack object. Print statement can print an object stack each item (if item is an object then place an empty column and advance to next one if find one in line or in next line). Function Len() work for all containers like string, stack object, pointer to array, inventory (list with keys or keys/values)
Flush
Data 1, 2 , 3 ,4
A=[]
Print A \\ 1 2 3 4
Print Stack.Size \\ 0
Print Len(A) \\ 4
We can place back to stack
Push 1, 2 \\ 2 is in top now
Stack \\ show 2 1
Stack A \\ merge bottom A, A is an empty stack now
Stack
Stack \\ show 2 1
Stack A \\ merge bottom A, A is an empty stack now
Stack
Print Len(A)
Clear \\ clear all variables from this module/ or all in line interpreter
A=Stack \\ define a new stack object
Stack A { Data 1,2,3,4,5} \\ in {} current stack is A
Print A \\ 1 2 3 4 5
B=A \\ B point to same object as A point.
A=Stack:=10,20,30 \\ A point to new object
Print Len(A), Len(B) \\ 3 5
Stack A {Read X:Stack B {Push X}} \\ pop from A and push to B
Print A \\ 20 30
Print B \\ 10 1 2 3 4 5
Stack A {Push StackItem(B)} \\ push a copy of top of B in A
Print A \\ 10 20 30
B1=Stack(B) \\ return a copy of B
Stack B {Drop}
Print B \\ 1 2 3 4 5
Print B1 \\ 10 1 2 3 4 5
Stack B1 {
ShiftBack Stack.Size \\ send top to bottom - Shift Stack.Size do the reverse
} \\ in a module or a function we can use multi line statements in blocks.
Print B1 \\ 1 2 3 4 5 10
Return B1, 6:=6 \\ Return can be used for a list of pairs of indexes/values
Print StackItem(B1, 6)
Print A \\ 10 15 20 25 30
Stack A {ShiftBack 1, -Stack.Size } \\ reverse order
Print A \\ 30 25 20 15 10
Stack A {ShiftBack 1, -Stack.Size } \\ reverse order
Print A \\ 10 15 20 25 30
\\ there is a symmetry with Shift if we use -Stack.Size
Stack A {Shift 1, -Stack.Size }
Print A \\ 30 25 20 15 10
Stack A {Shift 1, -Stack.Size }
Print A \\ 10 15 20 25 30
Stack A { Over Stack.Size, Stack.size} \\ double stack
Print A \\ 10 15 20 25 30 10 15 20 25 30
Clear \\ clear all variables - stack objects without a pointer to point to them erased
Passing a Stack Object as Parameter List
A=Stack:="H","E","L"B= Stack:="L","O"
Def Disp$(a$, b$, c$, d$, e$)=a$+b$+c$+d$+e$
Print Disp$(!A, !B), Len(A), Len(B) \\ Hello 0 0
Print Disp$("Y","E","S","","") \\ YES
Print Disp$(!stack:="Y","E","S","","") \\ YES
Print Disp$(!(stack:="Y","E"),"S","","") \\ YES
Print Disp$(!(stack:="Y","E"),!stack:="S","","") \\ YES
Print Disp$(!(stack:="Y","E"),!(stack:="S","","")) \\ YES
A=Stack:="H","E","L"
B= Stack:="L","O"
A=Stack:=!A, !B
Print A \\ H E L L O
Print Len(A), Len(B) \\ 5 0
B=Stack(A, A) \\ merge two copies of A as a new object for B
Print B \\ H E L L O H E L L O
Print Len(A), Len(B) \\ 5 10
Working with Groups in Stack
Because Groups are not reference entities in M2000, we have to work by using a For This {} (a block for temporary definitions), using a local N (shadows any N before), then extract a copy of group, do whatever with it, and then return the new one.Return statement has three variants. Without argument is as return from a Gosub to label number or letters/numbers. With a string as first argument return fields using a query in a database. With pointer (stack, array, inventory, buffer) return a list of data to specific position/index/key/offset.
Flush
Group Alpha {
x=10
}
K=Stack:=100, Alpha
For This {
Local N
N=StackItem(K, 2)
N.x++
Return K, 2:=N
}
Stack K \\ place K to current stack
Read Z, M
Print Z, M.x \\ 100, 11
Stack Object in Arrays/Inventories
The examples above are small so I wish to be without color, to make some identifiers in bold. But here it is more complicated so I get it with color (in Linux I use copy to clipboard, paste to LibreOffice, copy to clipboard and paste to textbox in Blogger, in browser. From Windows no need to use a "buffer" program, we can copy from M2000 editor to browser).A (,) is an empty "auto" array, a (1,) is an array with one item, a ((1,2),(3,4)) is an array with two items as arrays of two items. We can make arrays using Dim, or by using auto arrays. We can link a pointer A (pointer to array) to a name like A() to get another interface. An array used by pointer has different interface. If A=(1,2,3) then A+=10 make A as 11,12,13. In any interface we can't use arrays to perform mat operations (maybe in another version of M2000).
here we make three kind of objects, mStiva (Stack Object), mArray and Inventory. In key "100" in inventory B we put a shallow copy of A(), where reference types like stack object, just copied pointer to them. (Groups are not reference types, so they copied as values, but inside groups, members with reference types copied by pointer).
Arrays with parenthesis copied as values (shallow copy), but Arrays as pointers copied as reference type. X=Y() means X is a pointer to Y(), Z()=Y() means Z() is a shallow copy of Y(). Link X to B() means that we get two interfaces, and B() is reference to X, so if any change means the other change too, because is only one pointer, and two reference to that pointer. X=Y() and Z=Y() are two identifiers which point to Y(), so X=(1,2,3,4) make X to leave pointer to Y() and point to a new auto array.
A=Stack:=1,(2,3,4)
Print Len(A) \\ 2
Stack A {
Print Envelope$()
Print StackType$() \\ or StackType$(1) give Number
Print StackType$(2) \\ mArray - the M2000 Array
}
Print StackType$(A) \\ or StackType$(A, 1)
Print StackType$(A, 2) \\ mArray - the M2000 Array
Dim A(10)
A(2)=A
A(4)=100
Print Type$(A(2)) \\ mStiva - the M2000 Stack object
inventory B="100":=A() \\ give a shallow copy of array A()
Print Type$(B("100")) \\ mArray
Print Type$(B("100")(2)) \\ mStiva
B("100")(4)+=200
Print B("100")(4), A(4) \\ 300 100
\\ we get a pointer
Print Len(A)
\\ we dump B("100")(2) in N
N=stack:=!B("100")(2), 3,4,5
Print N \\ 1 3 4 5 - second column is just a space, because it is object
Print Len(N), Len(A), Len(B("100")(2)) ' 5, 0, 0
Z=N
If False Then { \\ change False with True
A=Stack:=!N \\ A get a new object, empty N
Print Len(N), Len(A), Len(B("100")(2)), Len(A(2)) ' 0, 5, 0, 0 \\ last two are 0, have old object
} Else {
Stack A {
Stack N \\ merge to bottom, N to A, N empty
}
Print Len(N), Len(A), Len(B("100")(2)) , Len(A(2)) ' 0, 5, 5, 5
}
Print Len(Z) ' Z=0 points to where N points.
Print Len(A) \\ 2
Stack A {
Print Envelope$()
Print StackType$() \\ or StackType$(1) give Number
Print StackType$(2) \\ mArray - the M2000 Array
}
Print StackType$(A) \\ or StackType$(A, 1)
Print StackType$(A, 2) \\ mArray - the M2000 Array
Dim A(10)
A(2)=A
A(4)=100
Print Type$(A(2)) \\ mStiva - the M2000 Stack object
inventory B="100":=A() \\ give a shallow copy of array A()
Print Type$(B("100")) \\ mArray
Print Type$(B("100")(2)) \\ mStiva
B("100")(4)+=200
Print B("100")(4), A(4) \\ 300 100
\\ we get a pointer
Print Len(A)
\\ we dump B("100")(2) in N
N=stack:=!B("100")(2), 3,4,5
Print N \\ 1 3 4 5 - second column is just a space, because it is object
Print Len(N), Len(A), Len(B("100")(2)) ' 5, 0, 0
Z=N
If False Then { \\ change False with True
A=Stack:=!N \\ A get a new object, empty N
Print Len(N), Len(A), Len(B("100")(2)), Len(A(2)) ' 0, 5, 0, 0 \\ last two are 0, have old object
} Else {
Stack A {
Stack N \\ merge to bottom, N to A, N empty
}
Print Len(N), Len(A), Len(B("100")(2)) , Len(A(2)) ' 0, 5, 5, 5
}
Print Len(Z) ' Z=0 points to where N points.
Advanced Tricks
This part isn't for Stacks, but explain some aspects of M2000 Interpreter, using the Return command for stack object.
A Set statement send line to CLI (the manual mode), but with current stack. The code below can be written in a module. We use an inner module with name Return. In optimization on, a reserved command can be used. and temporary disabled. Because Return has three uses, and one is for returning from a gosub (rare use) exist in interpreter main command list (a short one). So there is no way using @ to call Return (@ call using extended list, @Print always call Print). There is another way to call it. We use the Command line interpreter main command list (this interpreter has no commands like For or While), but has Return not for returning Gosubs but for returning fields in databases, and data in containers. All we have to do is to change namespace. This can be done with the use of Module and the name which we pass to stack and read with Letter$ (like Number for popping numbers from stack there is Letter$ for popping strings from stack)
A New command (erase all modules from memory) refresh list for commands always.
Optimization
on \\
normally we use optimization on
A=stack:=1,2,3,4
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 3 3 4
\\ making a module Return we hide Return as a M2000 statement
\\ normaly with a @ we call M2000 statements
\\ but Return isn't a normal identifier, exist in a higher priority list of commands
\\ like For and While
Module Return {
read K
Print k
}
Return 100
push module$ \\ push namespace to stack
\\ call cli, set namespace: execute return
\\ this happen because there are two interpreters, a heavy and a light one
Set Module letter$ : Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 4 3 4
A=stack:=1,2,3,4
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 3 3 4
\\ making a module Return we hide Return as a M2000 statement
\\ normaly with a @ we call M2000 statements
\\ but Return isn't a normal identifier, exist in a higher priority list of commands
\\ like For and While
Module Return {
read K
Print k
}
Return 100
push module$ \\ push namespace to stack
\\ call cli, set namespace: execute return
\\ this happen because there are two interpreters, a heavy and a light one
Set Module letter$ : Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 4 3 4
We can add this part.
Optimization
off
Gosub 100
Gosub 100
Optimization on
End
Gosub 100
Gosub 100
Optimization on
End
100 Print "ok"
110 Return
The same program using optimization off. Now we have to use Call Return to call Return (or use .Return - see dot before name). Optimization use a hash table. Without optimization interpreter fall to the old select case inner structure (is fast but not as hash table). Hash tables always used for variable names, and module names.
Optimization
off
A=stack:=1,2,3,4
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 3 3 4
Module Return {
read K
Print k
}
Call Return 100
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 4 3 4
optimization on
A=stack:=1,2,3,4
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 3 3 4
Module Return {
read K
Print k
}
Call Return 100
Return A, 2:=Stackitem(A,2)+1
Print A \\ 1 4 3 4
optimization on
Function name can't change! We can use dot before name.
Function
abs (s)
{
=s
}
Print abs(-10), .abs(-10) \\ 10 -10
\\ in one line
Def abs(s)=s
Print abs(-10), .abs(-10) \\ 10 -10
=s
}
Print abs(-10), .abs(-10) \\ 10 -10
\\ in one line
Def abs(s)=s
Print abs(-10), .abs(-10) \\ 10 -10
Δεν υπάρχουν σχόλια:
Δημοσίευση σχολίου
You can feel free to write any suggestion, or idea on the subject.