A simple example just open M2000 interpreter, then write Edit A, paste this code, then press Esc, and write A and you get the results.
About program.
1. Module ShowColors is a module in module A. Function Title$() exist as string funcion, but here we make a user function (which hide the M2000 function, for this module). We can delete this function, because Title$() exist and have same behavior. Also we can change a M2000 function adding more parameters, or change the signature as we wish. Each inner module/function can be called only from code inside module. We can't call a sibling module/function, but we can make some modules or functions as global (module Global Inner { ... })
We can make two type of functions. The simple functions, and the lambda functions (see 2)
We call the Module ShowColors after we declare it, using its name. At calling M2000 pass the current stack of values in a process object, among the code of chosen Module. So the next statement after the call is a READ, which read from stack of values. A read statement can make variables. If a global variable exist with same name then a new local variable created. If a local variable exist then first the READ check the type of that variable and the type of the top value on stack and then if its ok do the read else raise an error. To read a global variable we have to use a line to line interpreter using Set, so a Set Read Alfa read Alfa as a global variable (if exist assign value on it)
Module ShowColors has some parts. First is the function declaration of Tittles$(), then a Pen and a Cls (clear screen) set pen and background color. The next part make an Inventory, an object with keys and values. keys maybe numbers or strings (internal is always strings), and value can be anything (except simple functions and modules, but we can create Groups which have functions and modules).
Before using Module ShowColors we have to delete all values from stack of values, or we can do this using a temporary stack STACK NEW { ShowColors : ..... } and at the exit from the block we get the old stack, which preserve the Stack New { } block.
Here we use Flush to empty the stack. So the next part of Module ShowColors include some Data statements which place values to end of stack, so the first we place we read it first (FIFO). So to use the stack (which is a LIFO) as a FIFO we have to use Data (using Push we use stack as LIFO). For this example we can change the DATA with PUSH, and we get the list in reverse order, but this isn't a problem.
The next part has two For loops. The first append 16 pairs to inventory list. The second print a list of list keys and use the value to change the color using a Pen color_value {} block which preserve at the exit the pen color before the use of Pen. We use the For Next and not For { } block. We can use Next i or Next without variable name. A For always execute at least one (even with a zero step), at default behavior. We can change behavior using a proper switch, where we get the For like this on Basic language). At default behavior we use absolute value of step, so a For I=16 to 1 step 1 { } call the block 16 times and i get value 0 at the end of the loop. In alternate behavior the sign used so we get no execution of block, and the I get 16 as value. From a module we can use Set Switches "+FOR" to set the BASIC type For (or "-FOR" to set the M2000 type of For).
2. The final part has two statements. We make a lambda function as css_c and we push the value of it on stack of values.
Creating the lambda function is easy. We have to use lambda clause (if the returned value isn't a string, or something which return a string, like another lambda function), After the lambda we can place any closure. The closure is a copy of variable. but here the css_colors is an object pointer so we copy the pointer only. After the list of closures we can put a list of arguments (although this is a sugar code, and the list of arguments is a Read statement as first statement in lambda function body. In M2000 all user functions except Event functions have no signature for arguments, so we can pass anything and the Read statement can be raise the error, or we can read the types of stack and make whatever we want. In event functions because of multicast behavior (calling an event, which may have a list of functions, so multicasting means to passing to each function the same signature of parameters, as copies (including by reference parameters), so the signature checked before calling)
Inside lambda css_c we have a Try ok {} block. If we give a not exist key we get 0. Without the = statement (as first char) the function has the default Empty value. But we place a =0 before the try so we get the 0 as a double (or we can use =0& to return a long type, or 0%, for integer 16bit or 0@ dor decimal or 0# for currency or 0! for single float)
Look this example. k is a lambda but has no = statement. So the returned value is Empty. Variable N get the value Empty.
k=lambda -> {}N=k()Print 1, 2, 3, 4Print N, 0, k(), 1Print N+1, N*1, Type$(N)="Empty"
we get an empty column when we pass an Empty value, but Empty value is a 0 for calculations.
1 2 3 4
0 1
1 0 True
3. So after the return from Module, all entities from Module deleted except the lambda function and inside this the inventory which kept as closure. We check this using some Print statements inside Pen {} statements.
4. Now we make another lambda function, which return a string, so we use lambda$. A lambda which return a string, if we don't provide a = statement we get an empty string (not empty value).
Here we pass a closure of another lambda. When this lambda execute the function body make three local variables, a$, clr and clr$. The two last have different names, the second one has a $ which also means that is a string.
5. One last thing
For i=1 to 16
Print i, :Pen css_colors(i-1!) {Print Title$(eval$(css_colors, i-1))}
Next
An inventory has unique keys (except of the type of queue). If we delete a key then we loose the order of keys. We can sort the keys using the Sort statement. We can read a value using the name of inventory as a function. We can use the name of the inventory as a string function (using the $). So css_colors$("GRAY") return the number value as a string.
We can use the order of the key, using the ! after a number expression, where 0 is the first element. So we give css_color(ii-1!) to get values from 0 to 15. We can get the name of key using the ordinal number, using Eval$(css_color, i-1)
To find the ordinal number of a key we can use this (4th-1=3):
If exist(css_colors,"WHITE") Then Print eval(css_colors, !)=3
The inventory deleted when the last pointer for it also deleted. The closure css_c is a copy, but has a copy of the pointer of the inventory. M2000 Interpreter delete the variables from the last, so first delete the ExportCssColor$() which delete it closure css_c which delete it closure css_colors (because there is another pointer to inventory, the inventory still exist). After that delete the css_c which delete the css_color closure, which release the inventory because it is the last pointer.
6. The code (put it in a Module)
Flush ' Empty the stack of values
Module ShowColors {
Function Title$(a$) {
=Ucase$(left$(a$,1))+Lcase$(mid$(a$,2))
}
Pen 14
Cls #A0A022
Print "CSS COLORS"
\\ CSS colors
inventory css_colors
Data "BLACK", #000000, "SILVER", #C0C0C0
Data "GRAY", #808080, "WHITE", #FFFFFF
Data "MAROON", #800000, "RED", #FF0000
Data "PURPLE", #800080, "FUCHSIA", #FF00FF
Data "GREEN", #008800, "LIME", #00FF00
Data "OLIVE", #808080, "YELLOW", #FFFF00
Data "NAVY", #000080, "BLUE", #0000FF
Data "TEAL", #008080, "AQUA", #00FFFF
For i=1 to 16: Append css_colors, letter$:=number: Next
For i=1 to 16
Print i, :Pen css_colors(i-1!) {Print Title$(eval$(css_colors, i-1))}
Next
css_c=lambda css_colors (a$) -> {
=0
Try ok {
=css_colors(Ucase$(a$))
}
}
Push css_c
}
ShowColors
Read css_c
Pen css_c("Navy") {Print "Navy";string$("-", width-4);}
Pen css_c("Lime") {Print "Export html color from name"}
ExportCssColor$= lambda$ css_c (a$) -> {
clr=css_c(a$)
clr$=hex$(clr, 3)
=Right$(clr$, 2)+Mid$(clr$, 3,2)+left$(clr$,2)
}
Print "Aqua = #"+ExportCssColor$("Aqua")
Print "Red = #"+ExportCssColor$("Red")
Print "Gray = #"+ExportCssColor$("Gray")