Document your code

Every project on GitHub comes with a version-controlled wiki to give your
documentation the high level of care it deserves. It’s easy to create
well-maintained, Markdown or rich text documentation alongside your code.

Introduction to M2000

Interactive Mode

We can start m2000.exe at interactive mode, without providing a gsb file to execute. At a prompt > we give so called manual commands, and we can define global entities like variables, functions and modules.

>Print “Hello” Hello

All commands are in English and Greek words, and we can use either language per command. So we can use a module using English commands, in a module using Greek commands.

Console/Terminal

Internal terminal for manual commands. Use Linespace to insert spaces between lines.

Commands to show directories, files, search text in files.

Used mostly in full screen, but can be change in size, position and transparency. Also we can create path to exclude unwanted parts, and create a form of irregular shape.

Graphics/Text can be mixed. Also there is a Background layer, and 32 layer above console as sprites/layers.

Editor for modules/functions with automatically color coding.

Help pop up window.

Can change character resolution in any time. Form 80, 40

We can execute a gsb without GUI or Terminal. We need to insert a Show command or use Input to open Terminal.

We can use M2000's Windows instead of terminal, or in front of terminal.

We can execute Os commands, or execute applications using Win command using filenames and command.

Save name (without quotes) save all modules/functions in that name. Ctrl+A save to same file after the first save.

Statements: NEW, START, FLUSH, CLEAR (see Help, write Help New)

Code executed on a "global" runnable object. When we call a named block of code (say a Module or a Function) then a new runnable object created for code execution.

Special calling can pass in a new runnable object the same lexical scope from already running object. This used for event handlers, and for user call back (we can call the calling object, by running a new object using same lexical scope).

We can change static variables (or add new static variables) in a threat from other thread, using thread id and executing a command (basically we execute a statement which is not part of thread, but in thread's runnable object, which certain is in idle state)

We can use optional types for parameters in functions and in modules. We can use named parameters to pass values using % before name, so %K=100 is different from K=100 which place a Boolean. Both Modules and Functions may have optional parameters, and char ? means "no value". If a parameter has no value, because we didn't provide at call, and has no starting value, then an error occur.

Passing values can be done by value as standard. Some objects provide pointers, so they pass a copy of pointer. We can define by reference using &. References are strings, so we can place a string with appropriate information to the caller command. There is no optional for parameters by reference. Also for objects and if we use type at parameter list, there is no optional, we have to provide an object of the same kind. For containers using pointers, like pointer for array, for inventory and for stacks we can use as pointer for any of the three.

Runnable Objects

A runnable object uses lexical scope from module or function which execute.

Modules, functions and Threads executed on separate runnable objects. Threads are in same lexical scope with Thread Generator (a Module or a Function)

Any name generated in a runnable object deleted at the destruction time. So inner modules/functions are destroyed. Static variables are packed to parent runnable object to be used next time.

Stack for Values

A stack of values is an object special collection of values. Calling modules, functions, subroutines we pass arguments to a stack of values. This isn't used as return/process stack.

M2000 environment start with one stack for values, the global stack. Stack for values can hold any object/value. We READ from top (say position 1) and we put to top using PUSH, or at the end using DATA. So we can use it as LIFO or FIFO

Any module call any other module passing current stack values

Normally any function start with new stack for values

Using CALL we can call a function as Module (passing current stack)

We can open a temporary stack hiding current stack for values

Event Handler always use new stack for values

Passing by reference can be done, with a copy of a weak pointer.

We can pass by reference values/groups/functions but no modules/subroutines

A BASIC like Language

No case sensitive for names, except Labels

Use of $ and % at the end of names for strings and integers (can be any type, including double, with no decimal part)

Numeric computations done according to type (previous versions use double)..

Variables can by defined using a type or can be defined using the type as a result of an expression. Local Variables can' change type. One exception exist, when we pass a pointer of an object then variables change from numeric to objects. The other way isn't possible..

Array items have no types except for arrays of groups, when we defined as an array of that kind of object. We can place arrays in array items too, and pointers to objects and groups

Random Access files one field only (we can split field using commands)

Open command for files, Ansi (by default) and Wide when indicated with Wide clause. Using Locale statement we can change the way we read and write Ansi files. Wide files are in UTF16LE. Using Load.Doc for document type objects we can read any text file, using an internal algorithm to specify the type. We can direct Load.Doc to open in a specific format, and for Ansi we can specify the language id (1033 for english)

Using Document object we can save/load in Ascii (using Locale), Utf-8, Utf16LE and UTF16BE.

Optional Numbers in lines (can be used as labels too)

Labels (this is a label Label:). Labels with letters need a separate line, optionally comments after in same line.

Semi colon : split commands in line.

There is no Run command, we use module name instead. We can make a Module as Run, and we can place code inside this module. Then we can write Run (as module name) to execute this code. We can write some more "programs" and each one can be called by name.

File format GSB used for saving/loading M2000 scripts. They are in UTF-8 format, but we can load in any format as Document object support.

We can save code in scrabbled text, with a master key, provided by environment, or using own key. Using the master key we can run scrabbled scripts automatic

Basic Types

Double (as in Vb6)

Decimal (e.g. 1000@)

Currency (1000.23#)

Single (1.1212~)

Long (32bit, 100& or 0xFFFFFFFF&)

Integer (16Bit, 100% or 0xFFFF%)

Boolean (returned in comparisons as vbBoolean, but has double format as -1 and 0.

Integer Numbers (use % at the end of name), can be anyone from the above. An Decimal as an Integer (Name% is a name for this kind of variables). We can use hex as unsigned numbers 0xFFFFFFFF (using & or % at the we get the sign version for there literals).

String (it is a BSTR as in VB6 and a string data type that is used by COM) (use $ at the end of name)

Structures in Memory Buffers (unsigned Byte, Integer, Long and Double, Single and Strings (pointers to BSTR or in integer arrays in buffer block). Structures may have unions and other structures.

We can make from Basic Types:

Local Constants

Global Constants

Local Variables

Global Variables

Static variables (per runnable object)

Global Arrays

Local Arrays

Special Objects/Values

Document (linked list of paragraphs)

Event (Internal list of functions)

Enumerations (list of numeric constants with type the type of enumeration)

Lambda Function with closures

Auto Array, a nameless array, where (,) is an empty auto array.

Stack (for Values). We can move items from top to any position, and vis versa.

Arrays using () or without (as pointers to arrays)

Inventory, a list of keys, or keys and values. Unique Keys, can be string or numbers (internal strings). Use Hash Table.

Inventory queue, as Inventory but can take same keys

Buffer (memory chunk), we can make Structures for Buffers.

Com Objects

Form/Control (are com objects too, but are internal to environment)

Com Properties, as identifiers bind to an objects properties.

Import/Export

M2000 script file type is gsb as text in UTF8. Gsb files can be read in any of these encodings UTF8,UTF16LE, UTF16BE, ANSI, and with crlf, lf, lflf as line ending.

In scripts tab converted to 6 spaces at loading stage.

Open file in ANSI or UTF16LE format (using For Wide, in OPEN)

Can load/Save text in/from a Document object using UTF8,UTF16LE, UTF16BE, ANSI. (using Locale to specify the way to convert for Ascii)

Can open/make databases using DAO and SQL

Can read/write bmp and jpg files. Bitmap can be saved in strings. Png files and other types, using GDI+ can be loaded also in buffers.Png can be used as software sprites

Using Smooth statement (Smooth On/Off) we can use GDI+ for 2D graphics, using same commands for stantard GDI32 graphics

Can read ico, wmf, emf files.

Multimedia/Html

Video player. We can move back or in any position by time. We can set size of displayed video, and position. Video are played in terminal (console)

Music player. We can play wav and mid files. Also there is a Background statement with variation to play music in a background task.

Music Score Player. We can provide strings with a specific language for music score, for each voice (16 voices, the number 10 used for drum machine), assign an organ for each - except number 10. Score played using internal task manager.

Html Browsing. We can play radio, video, show images using Browser

Advanced topics

Block of code in curly brackets { }. Any block of code can be used for loop, using Loop command.

Multi line strings using curly brackets { }. Editor can know if content in curly brackets has commands or is a string.

Modules, Functions, Subroutines. May have Subroutines/Modules/Functions

There is no static modules/functions except for those Global modules/functions which first loaded. We can make temporary globals also.

Modules and Functions run on runnable objects. Subroutines are light parts of Modules and functions, without own runnable object (use the parent object)

A runnable object may have threads, other runnable objects, with parts of code to run in regular period of time, and we can handle them with a number (thread handler)

Modules/ Functions may have Static simple variables (not arrays with Brackets). Static list stay in parents runnable object, so a Module may have two or more set of static variables, if can be called from different runnable objects. A thread can see modules variables, but has own static variables, because it is a runnable object.

A runnable object, not a thread, may have functions as event handlers for forms and controls and for events from COM objects.

Lambda functions as first citizens

Groups as prototyped objects. Class produced a factory function for groups. SuperClass as a common entity for common entities. Groups may have variables, properties, modules,functions, events, arrays, groups in groups. Properties are groups also. Groups can get value, can return value, but by default return a copy of it, and merge a copy of any other. There is no Group Pointers.

Arrays with parenthesis, like A(), can be copied

Auto Arrays like ((1,2),(3,4)) is an array with two items, and each item is an array with two items.

Containers: Stacks, Arrays without parenthesis like A, Inventories (map type list),used by pointers to them. We can assign a group, or other value, or other container, in any position/index/key to those containers.

User Structures to define memory blocks, Buffers. We can place/read in/from buffers unsigned numbers (byte, word, long), double (8 byte), and strings (one or two bytes per char). We can make structures in structures and unions. Structures used to make Buffers, memory with specific start and size, as arrays of structures.

We can declare objects as COM objects. Also we can declare forms/controls (they are com objects too, but they are internal M2000 environment's objects). We can declare OS functions and other functions from DLLs using C calls.

We can use Javascript/VbScript from M2000, exporting M2000 object to them.

Special Behavior

We can change later a local function or a module definition with a new one.

Decoration: We can change a module B inside a local module A before a calling of A with a module defined in the current module.

Groups (objects in M2000) may have modules/functions that can be changed later.

Calling a module, interpreter never check the argument list, just send arguments using stack for values, so the calling module is responsible to leave stack in a normal condition. Maybe we want to return values. This hold for function called as modules using Call statement.

Calling a user function in an expression can get any argument list and function's code is responsible to read values. If some values left in stack for values isn't problem, because stack destroyed at the exit.

In any case above two paragraphs, there are ways to inspect stack before read values. This means that a module or function can read passing argument to parameters in stages, and not at the beginning of execution of their code.

Modules can't called recursive except we call them using Call statement.

Functions has recursion at a limit of 3250 calls (3250 runnable objects).

Subroutines if never use block of codes can have big number of recursion, and this can be set to 100000 or more, using a special statement Recursion.Limit.

Block of codes, using curled brackets { } have an internal flag to repeat execution, and can be set for one time with Loop command.

We can load modules, or edit modules (we can rerwrite code) at execution time.

There is Eval() and Inline statement to parse expressions and code at running time.

We can call vbscript or javascript passing special object to execute back commands from these script languages and execute vbscripts or javascript scripts with call back to module which call them.

We can declare functions from a dll with c calls, or stdcalls.

We can use COM objects, with WithEvents, and we can define functions with event names using names object underscore event-name. These functions use parent scope.

We can declare GUI forms and controls and get events in functions using object dot event-name and parent scope too.

Pointers to containers can be in List of variables, and in List of Static variables. We can get reference from static variables (from 9.5, using a copy in copy out mechanism). We can get reference to item of an array with parenthesis (from 9.5, using a copy in copy out mechanism).

Groups are objects with members (values, code as modules, functions, operators, value part, set part, and can have inner groups in any level) in one of two mode: As named group, each value member is as separate entity in list of variables, so we can get a reference from a member of a group. As in position in a container, so there is no member in list of variables, and all members are inside a private container in group. Interpreter has no way to read members in from a group in a container. To manipulate group, interpreter has to attach it to variable list, and deploy all modules/functions in a global Modules/Functions list, using a copy in - copy out procedure, so the group has to take name. Members of group which are containers copied just by pointer.

Sources of Code

There are eight places for source in M2000:

Command line in M2000 terminal (or console)

List of Modules/Functions

In list of variables/arrays, in groups, events and or lambda functions in containers.

In list of variables/arrays, in events and in lambda functions.

In list of variables/arrays, in arrays when an array initialized with a group for all items.

In current stack as a reference for function.

In a string. We can use it with Inline command, or for expression with Eval() and Eval$().

In Disk in files.

A group which have a name, has all code in List of Modules/Functions. A group module in that place can be decorated (altered inner modules from calling statement), like other modules in list of modules/functions

A function can be passed by reference, and this isn't the same as a reference in a value in list of values. A function pass code as reference, and if is a member of group, then pass after code the weak reference of group which belong. So when a new function created as a reference, get same code and same group name, so act as it is in group;

Event is a special object with a signature for parameters and a list of functions (as handlers of event), which can be passed any time. Event objects may be member of a Group.

Lambda functions

Lambda function is a special object, with one list of closures (variables), and a nameless function. A Closure is a private variable for lambda, which can be used in function, and hold value after the end of function execution. We make closures by using an existing variable, or a new one. Closures can be other lambda functions.

When a lambda get a name in list of variables, say A, then a name A() created in list of modules/variables. Actual lambda object is pointed in var() at an index pointed in the pair ("A", index1). In Function A() is a call to an extern function in var index same as index1. Because passing arguments are stay in stack of values, when we use the call to extern function we pass the same stack of values, so it is the same as we call the function in lambda, but code of lambda function is always in list of variables/arrays.

We can't get a second pointer for a lambda object. We get a new object, with a copy of closures (at the current state) and the code of function. We can use recursion using Lambda() or Lambda$() as name to call it, but closures get a copy in every call, so it is better to use a container, so in every call we get a copy with same pointer to container.

A lambda in a group has access to group variables. We can assign a new lambda in a group member which is lambda, and in code of new lambda we can use group members. In recursion, at each call lambda function has same access to group variables.

Unicode Support

Variable names can have any Unicode letter plus symbols dot, underscore, and non breaking space (nbsp), except symbols used for operators or other purposes. Internal editor colored properly variables when names follow the rules. So a variable may have space as nbsp and we can insert it using internal editor Shift Ctrl Space. (there is another space for numbers Shift Alt Space).

Unicode supported for filenames, for files (using Open with Wide tag), in forms, controls, terminal. Not supported right to left Unicode (we can displayed it only with Report statement and in editor, but not in Print statement).

Switches

We can alter the interpreter using switches (in command line, or by using Switches from M2000 terminal, or using Set Switches from code inside modules/funcitons. Use command Monitor to check switches. See Help Switches to see more info.

TXT

-TXT compare strings as binary (> < >= >= <> =) **by default**

+TXT compare strings as text (> < >= >= <> =)

DEC

-DEC use decimal point as dot

+DEC use decimal point as local (for code is always dot) ***by default***

DIV

-DIV like VB6 with integers.

Not the same for Double (mod return fractions too) ***by default***

Print 10 mod 2.3*2 is Print 10 mod (2.3*2)

+DIV like Python

Print 10 mod 2.3*2 is Print (10 mod 2.3)*2

PRI

-PRI ***by default***

Print True Or False And False Print (True Or False) And False

+PRI

And has priority over Or

Print True Or False And False Print True Or (False And False)

REC

-REC

300 limit for recursion for functions (if we strart m2000.exe from any other but the m2000.exe, we have to use Set Switches "-REC", because execution stack maybe has small size)

using For loop as normal in M2000, always execution of block, using from step the absolute value, if starting and ending number are different, or using normal value if starting and ending number are equal (to compute the final value for control variable)

+FOR

using normal sign step, and if sign of (ending point- starting point) isn't equal to step sign then we get no execution of for loop.

DIM

\\ only for current running interpreter

-DIM

normal a Dim A(4) has 4 items (0 to 3 or 1 to 4 depends of Base, by default is 0 to 3)

+DIM

if base 0 then we get for dimension items>0 one more, so a Dim A(4) has items from 0 to 4, so Len(A())=5

Also these arrays expands from last dimension (right one, but normal is from left one, or using Dim Ole make it from right one).

SEC

-SEC

Using interpreter as for version 8 and before. There are some specific conditions, name conflicts ,that we get wrong results, vary rare, so we use secure version, which changes internal names for modules and functions and there is no way to get conflict.