gnscript v1.5   by G. Nagler 2021  (c) GN Midi Solutions

a script language  

The script text is parsed into a recursive structure and interpretated without building an assembly code.
The script text may be encrypted with gnscriptcrypt.exe tool so that the file is binary and not readable for average user.
The script source may be binded with a script sum so that executing a modified script is not allowed when script sum is missing or does not match expected value.

=================================================================
LEGAL 
=================================================================
current state of gnscript program is for demonstration and testing only. 
It may not be used commercially.
No guarantees can be given at moment.
gnscript is currently tested with Windows 7, 8, 10, 11

=================================================================
COMMENTS
=================================================================
single line comments   begin with // ...  till end of line
multi line comments    begin with /* and end with next */
nested comments are not allowed:   e.g. START/* // /* */END ... */

=================================================================
TOKENS
=================================================================
LITERAL     "...."  or '....' or __multiline__("...") or __multiline__('...') or __noescape__('\[abc\]') or __escape__(...) or __singleline__(...);
    normal literals must not contain unencoded line breaks. multiline literals can be longer and can be more lines.
    backslashes and other special characters must be escaped in a literal e.g. \\  \n \r  \t \x7f
    using __noescape__ the literal does not escape any characters and the used quote character may not be used in the literal
    __escape__(xxx) and __singleline__(xxx) are default if not specifying __multiline__ or __noescape__
    __multiline__ and __noescape__ may be used together    __multiline__(__noescape__('text over many lines'))
    characters are same as string literals containing only one character
    
IDENTIFIER   alphabetic character sequence that may contain underscore _ and digits inside the identifier (can not begin with digit).
             identifiers are case sensitive.

NAMESPACEIDENTIFIER 
             an identifier for variable or function name that may optionally start with :: or could contain namespace references e.g. ::topnamespace::subnamespace::funcname(params)
             namespace can be used to make a global name more specific for a certain package to avoid conflicts with system function names or global variable names.
             using :::: between two names is invalid namespace  identifier ending with :: is invalid namespace
             (no "use namespace" statement is available, so if using name space then the full name space must always be used)
             
NUMBER       signed long integer number             -137
             hexadecimal long integer number         0x3af  (digits 0-9, a-f, A-F)
             number with optional exponent  10e3, 10.3e-3

BOOLEAN      true or false
             value not 0 or 0
             'yes' or 'no'
             'ja' or 'nein'

CONSTANTS
             null     (null object)
             true
             false
             pi
             e
             
             user applications may define more constants 


=================================================================
VARIABLES
=================================================================
var anydata, value = -3, string='abc',  vector = [1,2,3], structure = { a: 13, b: 'abc' };

1. local variables
2. function argument variables
3. global variables
4. system variables

Before assigning any value to a variable it is null and has undefined type.
Use keyword auto to make sure that the variable is defined and an already existing variable is reused (auto var maybealreadyexistingvariable;).
It gets a value and a type during assigning values.
Using a variable is in most cases a runtime error. 
Some exceptions where using undefined variable values is allowed:
    operator + allows concatenating undefined values with string values (but not for adding numbers). 
    ++ and -- operators assume value 0 for undefined variable value.
    == and != for comparing (possibly not initialized) variables against null
    isdefined(obj), isvariabledefined(obj) might be called using uninitialized or null variables
    
Variables with same name can exist in different scopes. Variables in inner scopes hide variables of same name in outer scopes. 

function func(v)
{
   // here it is v the function argument 
   {
      var v = v; // local variable copies the function argument value
      // variable v is not the function argument 
   }
   // here it is v the function argument again
}

=================================================================
CONSTANT VALUES FOR INITIALIZING
=================================================================
var stringv = 'a b c';
      functions __multiline__() and __escape__()  etc. can be used to use easier syntax
      for entering multi line text or text with or without escape sequences like \n
      
var number = 13;
var hexnumber = 0x3a;
var floatingnumber = 3.14; // 3. or .01 are not floating point numbers

Hexadecimal notation in literals are never converted to numbers (they remain strings). 
var hexstring = '0x3a';

But you can force the conversion to a number by cast operator (int)hexstring or (long)hexstring.

var vectorv = [];
var vectorv = [1,'a',3..5]; // produces a vector that contains 1,'a',3,4,5

var structurev = {a: 13, b: 'xy'; c: '00.00'; 12: 'z'; ' ': 'blank'; }

Structure keys are strings. when using search functions for structures comparing is string equality. 


=================================================================
TYPES
=================================================================
null   (undefined or uninitialized)
string (string or any number)
vector of objects (index values 0,1,2...size-1)
structure of objects to objects (map)
reference to an object  (usually used for reference parameters in functions)
user objects that are based on above types, implementation can define self if value or reference should be copied during assignment or using as parameter  

A variable automatically gets the type of an assigned value.
A variable type can be determined using cast type operators:
        (string)value, (long)value, (double)value, (vector)value, (structure)value

A string type expression can be also a number (long or double). It remains an unmodified string till a number operation is used:
    var x = '0001.0';  // remains a string as long as it is not used by a number calculation
    var y = x+1;   // the x is converted to number 1 and adding 1 results in 2 (after this still can be used as string or as number)
    
    var x = '0001.0';
    var y = (string)x+1;  // x is casted to string and a 0 character is added by string concatenation. Result y is '0001.01' but can be used as string or number  same as x was used.



=================================================================
EXPRESSION RESULTS
=================================================================
exception    (error structure with message and source location where it occured: e.what, e.pos, e.linenr, e.columnnr, e.filename, e.info, e.isfatalerror)
null         (no result)
boolean      true (also number != 0, 'true', 'yes', 'ja') or false (number 0, 'false', 'no', 'nein')
object       (can be casted to string)

============================
FUNCTION DEFINITION
============================
function functionname(param1,&param2, ...)

parameters are copied by default when calling a function. parameters with & are used by reference (not copied, any changes are also in the original scope variable).

Variable arguments are proviced by l-value (not copied). The optional arguments before the variable arguments must exist and are not necessarily l-values.

some functions have variable number of arguments (...), e.g. print('found ', count, ' items.');

When using variable arguments (...) parameter then a vector argument with name variableargs exists and is a vector of variable parameters. 
Function getfunctionargs() can be used to get a structure of all current function argument variables including variableargs if the function has ... arguments

function myvarargsfunc(a, ...)
{
   var result = a;

   for (var i = 0; i < variableargs.size(); i++)
   {
       var parami = variableargs[i];
       result = max(result, a);
   }

   return result;
}

function myvarargsfunc(a, ...)
{
   var args = getfunctionargs();
   println("all function arguments: ", args);
   println("a=", args.a);
   println("variableargs=", args.variableargs);
   
}
============================
CAST TYPES
============================
Following cast types can be used:
             long, double, float, int, string, structure, vector
var x = (long)'00003.14';    // x becomes a long 3

Function gettype(obj)  returns the current type of an object or expression
((long)'00003.14').gettype() returns string long

(vector)null casts to empty vector 
(structure)null casts to empty structure

Casting a structure that contains key numbers 0..n-1 to a vector results in a vector of the structure values indexed by 0..n-1 only if all index values are in the structure.
  e.g. {0:10,2:11,1:13}  casts to vector [10,13,11]

Casting an associative structure to vector results in a vector of structure elements
  e.g. {a:10,b:0,c:7} casts to vector [{a:10},{b:0},{c:7}]
  
============================
FUNCTION CALLING
============================
Functions can be called following 2 ways:
     funcname(param1, param2, param3);
        or
     param1.funcname(param1,param2);

Both have same meaning.
e.g. 3.14.sqr() == sqr(3.14)

Second calling method should be used when left side is a variable.
e.g. list.push(value);

Available functions are only searched by name and number of the caller arguments. It does not consider their types or contents. It is not allowed to define a function with same name and same number of arguments twice.
Functions can only be defined in global scope. Local functions are not supported. Function prototypes are not supported. Available functions are known directly after parsing before executing first statement.

Functions are found in following order:
1. function definitions by function funcname(arguments)
2. user functions defined in user extensions in order of added extensions
3. system functions

It is allowed to redefine a function that already exists as system function e.g. function min(a,b,...) { ... return myminimum; }


============================
VECTORS 
============================
Vectors are arrays of a certain size indexed by a number between 0 and vector.size()-1

v.size()
v[3] = value;   
v.push(value); // add value as last element
value = v.pop(); // remove last element
value = v.shift(); // remove first element
v.unshift(value);  // insert value as first element
v.insert(index, value); // insert value at position index, if index == v.size() appends the value to end

v['text'] = value  produces an entry in a structure same as  v{'text} = value;
Hint: vector init supports constant numbers and number range and strings and character ranges
   e.g. var v = [3,4..7,'word','A'-'Z'];


BNF syntax
==========



<gnscript> ::= <statements>

<statements> ::= <statement> [ ';' <statements> ]

<statement> ::= <ifstatement> 
                | <switchstatement>
                | <forstatement>
                | <whilestatement>
                | <dowhilestatement>
                | <repeatuntilstatement>
                | <foreachstatement>
                | <loopbreakstatement>
                | <loopcontinuestatement>
                | <gotostatement>
                | <gotolabelstatement>
                | <vardefinestatement>
                | <undefinestatement>
                | <functionreturnstatement>
                | <functiondefinitionstatement>
                | '{' <statements> '}'
                | <trycatchstatement>
                | <expression>
                
<ifstatement> ::= 'if' '(' <expression> ')' <statement> 
                  [ 'else' <statement> ]

<switchstatement> ::= 'switch' '(' <expression> ')' 
                      '{' 
                      <caseliststatement>*
                      '}'

<caseliststatement> ::= 'case' <expression> [ '..' <expression> ] ':' 
                        | 'default' ':'
                        | <statement>

                      
Hint:  case 13..224:  is a number range.  case 'a'-'z': is a character range

<forstatement> ::= 'for' '(' <expression> ';' <expression> ';' <expression> ')'
                    <statement>

<whilestatement> ::= 'while' '(' <expression> ')' <statement>

<dowhilestatement> ::= 'do' '{' <statements> '}' 'while' '(' <expression> ')'

Hint: repeat statements while expression is true (or break or last or return or exception)

<repeatuntilstatement> ::= 'repeat' '{' <statements> '}' 'until' '(' <expression> ')'

Hint: repeat statements until expression is true (or break or return or exception)

<foreachstatement> ::= 'foreach' '(' ['var'] IDENTIFIER 'in' <expression> ')' <statement>

Hint: it is not allowed to assign values to the foreach variable.

<loopbreakstatement> ::= 'break' | 'last'
Hint: last and break have same meaning and exits current loop

<loopcontinuestatement> ::= 'continue' | 'next'
Hint: continue and next have same meaning to continue with the next loop step
Hint: for(init;condition;step) continues with step command
Hint: while (condition) continues with condition
Hint: foreach (var v in collection) continues with next element v from collection)
Hint: do { statements } while (condition) continues with statements (not with condition)
Hint: repeat { statements } until (condition) continues with statements

<gotostatement> ::= 'goto' IDENTIFIER
              
<gotolabelstatement> ::= IDENTIFIER ':'

<vardefinestatement> ::= ['auto'] 'var' NAMESPACEIDENTIFIER [ '=' <expression> ]

<undefinestatement> ::= ['auto'] 'undef' NAMESPACEIDENTIFIER [ '=' <expression> ]

<functionreturnstatement> ::= 'return' [ <expression> ]

<functiondefinitionstatement> ::= 'function' NAMESPACEIDENTIFIER '(' [<functionparamlist>] ')' '{' <statements> '}'

<functionparamlist> ::= <functionparam> [ ',' <functionparamlist> ]

<functionparam> ::= [ '&' IDENTIFIER ]

<trycatchstatement> ::= 'try' '{' <statements> '}' 'catch' '(' ['var'] IDENTIFIER ')' '{' <statements> '}'

<expression> ::= <assignmentexpression> [ ',' <expression> ]
                
<assignmentexpression> ::= <questionexpression> [ <assignmentoperator> <assignmentexpression>]

<assignmentoperator> ::=  '=&' | '=' | '-=' | '+=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>='

Hint: =&  is reference assignment
Hint: %=  is modulo
Hint: &= is bit-and assignment
Hint: |= is bit-or assignment
Hint: ^= is bit-xor assignment
Hint: <<= is bit shift left assignment
Hint: >>= is bit shift right assignment
                          
<questionexpression> ::= <logicalorexpression> [ '?' <questionexpression> ':' <questionexpression> ]

<logicalorexpression> ::= <logicalandexpression> [ '||' <logicalandexpression> ]

<logicalandexpression> ::= <bitorexpression> [ '&' <bitorexpression> ]

<bitorexpression> ::= <bitxorexpression> [ '|' <bitxorexpression> ]

<bitxorexpression> ::= <bitandexpression> [ '^' <bitandexpression> ]

<bitandexpression> ::= <equalityexpression> [ '&' <equalityexpression ]

<equalityexpression> ::= <relationexpression> [ <equalityoperator> <relationexpression> ]

<equalityoperator> ::= '==' | '!=' | '=~' | '!~' | 'in'

<relationexpression> ::= <shiftexpression> [ <relationoperator> <shiftexpression> ]

<relationoperator> ::= '<=' | '<' | '>' | '>='

<shiftexpression> ::= <additionexpression> [ <shiftoperator> <additionexpression> ]

<shiftoperation ::= '<<' | '>>'

<additionexpression> ::= <multiplicationexpression> [ <additionoperator> <multiplicationexpression> ]

<additionoperator> ::= '+' | '-'

Hint: binary + operator prefers adding numbers than concatenating strings

<multiplicationexpression> ::= <unaryexpression> [ <multiplicationoperator> <unaryexpression> ]

<multiplicationoperator> ::= '*' | '/' | '%'

<unaryexpression> ::= '+' <unaryexpression>
                     | '-' <unaryexpression>
                     | '!' <unaryexpression>
                     | '~' <unaryexpression>
                     | '++' <unaryexpression>
                     | '--' <unaryexpression>
                     | '(' <castoperator> ')' <unaryexpression>
                     | '(' <expression> ')'
                     | <factor> [<postfixexpression>]

Hint: only ++ and -- and string + allow using undefined values. 

<castoperator> ::= 'long' | 'double' | 'float' | 'int' | 'string' | 'structure' | 'vector'

<factor> ::= NUMBER 
            | LITERAL 
            | CONSTANT 
            | <functioncallstatement>
            | NAMESPACEIDENTIFIER
            | <structureinitexprssion>
            | <vectorinitexpression>
            
            
functioncallstatement ::= NAMESPACEIDENTIFIER '(' functionparamlist ')'            
            
functionparamlist ::= <assignmentexpression> [ ',' functionparamlist ]

<structureinitexprssion> ::= '{' <structureinitlist> '}'

<structureinitlist> ::= IDENTIFIER ':' <questionexpression> [ <structureinitlistseparator> structureinitlist ]

<structureinitlistseparator> ::= ',' | ';'

<vectorinitexpression> ::= '[' <vectorinitlist> ']'

<vectorinitlist> ::= <questionexpression> [ '..' <questionexpression> ] [ ',' <vectorinitlist> ]

<postfixexpression> ::= 
            | <structureaccessexpression>
            | <vectoraccessexpression>
            | '++'
            | '--'

<vectoraccessexpression> ::=  '[' <expression> ']'

<structureaccessexpression> ::= '.' IDENTIFIER 
                             | '{' <expression> '}'
                             | '.' <functioncallstatement>
                        


SYSTEM CONSTANTS

true=1, false=0, null=undefined
pi=3.14159265358979323846,e=2.71828182845904523536
FILE_ATTRIBUTE_READONLY,
FILE_ATTRIBUTE_HIDDEN,
FILE_ATTRIBUTE_SYSTEM,
FILE_ATTRIBUTE_DIRECTORY,
FILE_ATTRIBUTE_ARCHIVE,
FILE_ATTRIBUTE_DEVICE,
FILE_ATTRIBUTE_NORMAL,
FILE_ATTRIBUTE_TEMPORARY,
FILE_ATTRIBUTE_SPARSE_FILE,
FILE_ATTRIBUTE_COMPRESSED,
FILE_ATTRIBUTE_OFFLINE,
FILE_ATTRIBUTE_REPARSE_POINT,
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
FILE_ATTRIBUTE_ENCRYPTED,
FILE_ATTRIBUTE_VIRTUAL,


REFERENCES

The default behaviour of assignment is copying the content for standard data types (string, numbers, vector, structure). 
The special reference assignment operator (=&) can be used to use a reference to the original object (var b =& a).
User data types defined in libraries can decide that an object is to copy by reference. It can also define other behaviour for r-value copying or forbid it. This is usually used when an object is bound to internal data in an application. The structure or vector object only gives possibilities to access or modify this internal data in a common practice instead of defining functions. E.g. openfile function fileopen(...) creates a structure that can only be used as long it remains an open file structure. Therefore copying is done by reference only. var file1 = file2;

E.g. a user application adds objects that can read and write to an own data structure  or database. 
It should be avoided to copy these objects since the copied structure could be big and has not anymore the same possibilites as the user application data structure (e.g. certain functions can only be called with the original objects and not with standard vector or structure).

To force to copy such an object to a standard type a compatible cast operator must be used e.g. var struct = (structure)userobj .
In most cases this is not useful. 

FUNCTION DEFINITION

function functionname(param1,&referenceparam2)

functions may not be defined inside functions

FUNCTION CALLING

var ret = functionname(value1, variable2);

or identical if value1 is an object 

var ret = value1.functionname(variable2);

Following order is used for calling:
1. user function if defined and matching parameter number
2. user application function 
3. system function

system functions and user application functions support also variable arguments e.g. max(1,3,5,7,pi)

By default all parameters are copied to new parameter variables. Use &param in the function header to use references to the original objects. 
Reference parameters modify values in the caller variables and speeds up calling by avoiding copying.

======================================
system functions
======================================

CODE FUNCTIONS

__VERSION__()
   gnscript version
__USEVERSION__(version)
   require a minimum gnscript version
abort(message)
abort()
die(message)
die()
   abort script optional with error message (default: empty message)
exit(exitcode)
exit()
   abort script with application exit code (default exitcode 0)
throwexception(reason)
sleep(milliseconds) 
   only waits
wait(milliseconds) 
   waits while system can do some work
allowmessageprocessing() 
   shortly wait that system can do some work
__LINE__()  
   current line number 
__LINE__(sublevel)
   current line number of some caller
__POS__()
   current source position 
__POS__(sublevel)
   current source position of some caller
__FILE__()
   current source file name
__FILE__(sublevel)
   current source file name of some caller
__FOLDER__()
   current source file folder path
__FOLDER__(sublevel)
   current source file folder path of some caller
__FILE__(sublevel)
   current source file folder path of some caller
__CODE__()
   current source code line 
__CODE__(sublevel)
   current source code line of some caller
__CODEPOS__()
   current filename and line and column position and source code line 
__CODEPOS__(sublevel)
   current filename and line and column position and source code line of some caller
getdebuginfo(obj)
   get some debug information about the obj
gettype(obj)
   returns s type name for the value (can be null, long, double, string, vector, structure)
getenv(varname)
   get the value of environment variable (e.g. PATH, USERNAME)
setenv(varname,value)
   set the value of environment variable
eval(code)
   evaluate gnscript source code using current context (variables, functions available can be called). Calling an invalid code inside a script causes an exception that can be catched with try {} catch(var e) {}
gnscriptexecute(scriptpath)
   execute the script in a file (similar to eval from string).
callfunction(functionname, ...)
   calls a function by name (inner scope has priority, variable arguments are function parameters, parameter count must match with existing function)

callfunction(functionheader, ...)
   calls a function by function header (e.g. 'myfunc(arg1,...)'   inner scope has priority, variable arguments contain the function parameters. Functions with not matching parameter count are ignored, if more parameters are specified in call then they are ignored  e.g. 'myfunc()'  will ignore extra parameters added in call.
  
include(filename)
   include a .gnscript source code from other file
   
uselibrary(libraryname,...)
   load one or more dll from given path (default extension .dll is automatically added)
   libraries search order:  absolute path name, library folders from setlibrarypath(), current folder, source code folders, interpreter application folder, system executable path
   currently only the main interpreter can call libraries, libraries can not use other libraries

setlibrarypath(libfolder,...)
   add folders to library path

getlibrarysummary(libraryname)
   get information about a loaded library
   uselibrary must have been called successfully 
   gives an overview about available function headers, constants, library variables
   e.g. uselibrary('gnscripttestlibrary'); println(getlibrarysummary('gnscripttestlibrary'))
   
TEXT FUNCTIONS

tostring(obj)
   convert obj to text
hexdump(obj)
   convert obj text to hexadecimal character numbers (can help to display binary data during debugging)
translate(text, context)  
   translate text using existing .translate files, use context for translating same text different for different purposes
translate(text)
   translate text using existing .translate files, use global translation context
strlen(text), 
length(text), 
size(text)
   length of text (multicharacter encoded, number of total bytes)
reservespace(text,len)
   when appending something often to text then reserving space might speed up the operation because reallocation memory is not necessary every time
comparetext(a,b)
    returns -1 if a < b or 0 if a == b or 1 if a > b
comparetextignorecase(a,b)
    returns -1 if a < b or 0 if a == b or 1 if a > b  for uppercase text a and b
comparenumeric(a,b)
    returns -1 if a < b or 0 if a == b or 1 if a > b  for numbers a and b or text a and b (if a or b is no number)
getarg(varname)
getarg(varname,defvalue) 
   safe method to get variable by name, get empty string or defvalue if variable was not set
   for command line arguments the options can be used:
       -myoption1=myvalue          getarg("myoption1")  delivers string object "myvalue"
       -myoption2                  getarg("myoption2")  delivers boolean object true
       input.mid output.mid        getarg("param1")  delivers string input.mid   and getarg("param2") delivers string output.mid  
                                   getarg("scriptfilename") delivers the script name parameter
   without using commandline the function getarg can be used to access global variables already set by the application by before the script has started
   e.g. getarg("input")  returns the value of global variable input       using input in the script would cause a syntax error if variable input is not defined. Alternatively isvariabledefined("input") could be asked before using variable input.
getargs()
     returns a structure that contains all options with values and parameters param1, param2 ... and scriptfilename
getglobalvariables()  
     returns a structure with global variables and references to that variable values
getfunctionargs()
     returns a structure with function arguments, variable args are in member with key variableargs
mid(text,pos)
    copy characters from position pos till end of text
mid(text,pos,len)
    copy max len characters from position pos of text
left(text,len)
    copy max first len characters from text
right(text,len)
    copy max len characters from end of text
fill(text, pos, size, filltext)
    fills all characters of text inside the given area by filltext 
index(text,subtext)
    find first matching character position of subtext inside text
    -1 if subtext is not found inside text
index(text,subtext, offset)
    find first matching character position of subtext inside text beginning at offset or behind
    -1 if subtext is not found inside text
containstext(text,subtext)
textcontains(text,subtext)
    check if text contains subtext
textstartswith(text,subtext)
    check if text begins with subtext
textendswith(text,subtext)
    check if text ends with subtext
rindex(text,subtext)
    find last matching character position of subtext inside text
    -1 if subtextis not found inside text
rindex(text,subtext,offset)
    find last matching character position of subtext inside text at offset or before
    -1 if subtextis not found inside text
strcmp(text1,text2)
    compare text1 and text2 case sensitive
    0 if identical  
    <0 if first different character of text1 is before character in text2
    >0 if first different characterof text1 is after character in text2 
stricmp(text1,text2)
    compare text1 and text2 case insensitive (A is same as a)
    0 if identical
    <0 if first different character of text1 is before character in text2
    >0 if first different characterof text1 is after character in text2 
trim(text)
    remove spaces at beginning and end of text
    space character, new line, carriage return, tab character are spaces
trimleft(text)
    remove spaces at beginning of text
trimright(text)
    remove spaces at end of text
shortcut(text, maxlen)
    create a shorter text by cutting the text and adding ...
shortmiddlecut(text, maxlen)
    create a shorter text by cutting and inserting ... in middle
escapestring(text)
    escape all special characters using \ sequences  (\t for tab, \n for newline, \r for return, \\ for backslash ...)
unescapestring(text)
    replaces escaped sequences against special characters e.g. \t against tab char \n against newline \\ against backslash ...
quotestring(text)
    add quote characters (") before and after the text if the text contains special characters or the quote character self.
unquotestring(text)
   if string starts with quotes " or ' then quotes are removed and text unescaped (text might contain e.g. \" as escaped character) 
ord(text)
    calculates the character number of first text character
    text should have length 1
isnumber(text)
    check if text is a number (integer or decimal)
ishexnumber(text)
    check if text is a hexadecimal number (only uses characters 0-9 and a-f,A-F)
isalpha(text)
    check if all text characters are ascii characters  
isalphanumeric(text)
   check if all text characters are ascii letters or digits
iswhitespace(text) 
   check if all text characters are spaces, tabs, newlines, linefeed
chr(value)
   convert the character number to a string that contains this character
toupper(text)
   convert all text characters to upper case (a-z to A-Z)
tolower(text)
   convert all text characters to lower case (A-Z to a-z)
split(text)
   split text into vector of fields assuming separator ,
split(text,separator)
   split text into vector of fields using separator
splitlines(text,trimends)
   split text into vector of lines (newline remains at end of each line)
   if trimends is true all lines are trimmed at end
splitwords(text)
   split text into a vector of words using spaces, tabs, newlines comma semicolon colon as default word separator
splitwords(text,delimitercharacters)
   split text into a vector of words using the delimiter characters in the delimitercharacters string parameter
like(text,pattern)
   check if text matches the pattern 
   pattern can contain wildcards * (any characters) ? (any single character) # (any digit)
join(separator,vector)
   join all elements in vector together to one text using separator between
join(separator,text1,text2,...)
   join all specified text together to one text using separator between
concat(vector)
   concatenate all vector elements together without separator
concat(text1,...)
   concatenate all text together without separator
replace(text,from,against)
   replace all matching from parts inside text against an other text
defaultreplaceoptions()
   returns a structure with default replace settings used in replace(text,from,against)
replace(text,pattern,replace,options)
   replace all matching from parts inside text against an other text
   options is an optional structure with optional boolean elments ignorecase, regularexpressions, wordsonly, wholeonly, replaceall, encodenotascii as delivered by function defaultreplaceoptions()

parseintrangelist(text)
   reads a list of numbers or number ranges and writes it into a vector (e.g. 1,2 3;5-7,9  is vector [1,2,3,5,6,7,9]

CSV FUNCTIONS

makecsvline(stringvector,fieldseparator)
    build a csv line string using allowed separator characters fieldseparator separfrom the data fields in vector stringvector
parsecsvline(csvline, fieldseparator)
    converts a valid csvline into a vector
    fieldseparators contains the possible csv separator characters e.g. ",;"
parsecsvline(csvline)
    converts a valid csvline into a vector
    field separators can be: , ; : tab
splitcsvlines(text, quotecharacters)
    split csv text using possible quote characters in quotecharacters to csv lines
    a csv line may be over multiple lines when using quote character
    quote characters are usually  " and '    quotecharacters="\"'"
splitcsvlines(text)
    split csv text using possible to csv lines
    a csv line may be over multiple lines when using quote character
    possible quote characters are: " and '

JSON FUNCTIONS

parsejson(text)
   converts a text in format JSON to an object e.g. 
createjson(obj)
   converts a structure or vector or string to JSON text e.g. defaultreplaceoptions() are converted to JSON:
{
    "encodenotascii": 0,
    "ignorecase": 0,
    "regularexpressions": 0,
    "replaceall": 1,
    "wholeonly": 0,
    "wordsonly": 0
}

REGULAR EXPRESSION FUNCTIONS   

match(text,regex,...)
   check if text matches a regular expression regex
   first optional argument contains variable for the whole match
   next optional arguments contains variable for group match enclosed in regex using ( and )
match2(text,regex,options,...)
   check if text matches a regular expression regex
   options are letters in the string: i for ignore case, w for words only, g for global 
   first optional argument contains variable for the whole match
   next optional arguments contains variable for group match enclosed in regex using ( and )
Hint: usually the regular expression is written in a literal so the backslash characters must be escaped. Always think what should a variable after assignment contain so that this text can be used as regular expression. 
e.g. 
    var wholematch, devicename;
    var regularexpression = "\\[device name\\]\\s*\(.*\)$"; // \[device name\]\s*(.*)$
    if (line.match(regularexpression, wholematch,devicename))
    {
       // use devicename
    }
    \\[  and \\] are required because the characters [ and ] should occur and not a regular expression character range.
    \\s  in literal ( \s regular expression ) means any space character (space or tab or newline or linefeed) 
   \\( would only match the character ( and not define a group
   
substitute(text,regex,against)
   replace longest matching regex pattern against new text
   regex may reference groups using (...)
   against may reference groups used in regex  $1 $2 ...  ($0 references whole match)
substitute(text,regex,against,options)
   replace first matching regex pattern against new text using option character list 
   option i uses case insensitive matching (default is case sensitive)
   option g uses replacing all matching parts (default is replace first longest matching part)
   regex may reference groups using (...)
   against may reference groups used in regex  $1 $2 ... ($0 references whole match)
   

OBJECT FUNCTIONS

isdefined(variable)
isdefined(variablename)
isvariabledefined(varname)
   checks if variable is defined
isempty(obj)
   tests if the object is empty (can be a string or vector or structure or or null)
clear(variable)
    set variable value empty
duplicate(obj)
   create a copy of an object
   
COLLECTION FUNCTIONS

push(vector,value)
   append a copy of a value to a vector object
pushreference(vector,refvalue)
   append a reference to a vector object
pop(vector)
    removes an item from the back side of vector 
shift(vector)
    removes an item from the front side of vector
insert(vector,index,value)
    inserts value at position index of vector
unshift(vector,value)
    inserts value at front side of vector
containskey(structure,key)
    check if structure contains key 
containsvalue(obj,value)
    check if vector or structure contains this value
    check if string contains value as substring
removekey(structure,key)
    remove key from structure
    remove element with key index from vector
removevalue(structure,value)
    remove structure elements matching value
    remove vector elements matching value
    remove first substring matching value from string
collectionkeys(structure)
   get a vector of all key names from the keys used in structure
collectionvalues(structure)
   get a vector of all values used in the structure
grepkeys(structure,operator,value,...)
   search key names of a structure that matches specified value compares
   compare operations are combined using AND and evaluated from left to right
   compare operators are:  
       equal: ==, =, (default: empty assumes equal operator)
       not equal: !=,!,<>
       relational: >,>=,<,<=
       in collection: in,!in
       regular expressions: =~,!~
grepvalues(collection,operator,value,...)
   search key values of a structure or vector that matches specified value compares
   compare operations are combined using AND and evaluated from left to right
   same operators as in grepkeys 
sort(vector)
   sort all values of a vector numerically and alphabetic
sortnumeric(vector)
   sort all values of a vector numerical (if values are numbers),data that are not sortalphabetic(vector)
   sort all values of a vector alphabetic
   numerical are compared alphabetic ignoring case letters
          23a is no number,-23,-23.4 are numbers
sortignorecase(vector)
   sort all values of a vector alphabetically ignoring case letters A == a
sort(vector,comparefunctionname,...)
   sort all values of a vector using an existing function with given name that has two arguments
   empty comparefunctionname compares numerical
   compare function must compare two objects and return true if the first value should be before the second value (lessthan implementation)
   the user function should use reference arguments lessthan(&a,&b) that unnecessary copying is avoided
inversesort(vector)
    sort all values of a vector numeric and alphabetically in inverse order (Z to A)
inversesortalphabetic(vector)
    sort all values of a vector alphabetically in inverse order (Z to A)
inversesortnumeric(vector)
   sort all values of a vector numerical (if values are numbers) in inverse order (9 to 0)
inversesortignorecase(vector)
   sort all values of a vector alphabetically ignoring case letters A == a in inverse order 
inversesort(vector,comparefunctionname,...)
   sort all values of a vector using an existing function with given name that has two arguments in inverse order
   empty comparefunctionname compares numerical
   compare function must compare two objects and return true if the first value should be before the second value (lessthan implementation)
   the user function should use reference arguments lessthan(&a,&b) that unnecessary copying is avoided
   extra parameters might be added to the sort function after the function name parameter
   they will be added when calling the compare function
   e.g.  sort(v,mycompare,compareoptions);
   function mycompare(&a,&b,&compareoptions) { return a < b; }
   

UNITTEST FUNCTIONS
unittest_clear()
   reset the previous unit test results and counter
unittest_count()
   get the number of tests called e.g. unittest_areequal(value, correctvalue, testname)
   this count can be used to guarantee that all unit tests of a script are really executed
   e.g.  last unit test can be unittest_areequal(unittest_count(), 10)  if the module should execute 10 unit tests
unittest_results()
   get the failed unit test result (each line looks like unit test TESTNAME failed:  reason)
unittest_areequal(value, correctvalue, testname)
   test fails if string converted value does is not equal to string correctvalue
unittest_areequalvalues(value, correctvalue, testname)
   test fails if long value is not equal to long correctvalue
unittest_arenearvalues(value, correctvalue, maxdistance, testname)
unittest_areequalbool(value, correctvalue, testname)
   test fails if boolean value is not equal to boolean correctvalue
unittest_mustcontaintext(value, text, testname)
   test fails if value does not contain text
unittest_isinvaluelist(value, correctvaluevector, testname)
   test fails if value is not equal to one of the vector elements
unittest_isinvaluelist(value, correctvaluewithseparator, testname)
   list of correct values is separated by character | . Test fails if value is not equal to one of these values.
unittest_mustcontaintext(value, text, optionignorecase, testname)
   test fails if value does not contain text considering optionignorecase true or false
unittest_istrue(value, testname)
   test fails if value is not true
unittest_isfalse(value, testname)
   test fails if value is not false
unittest_fileexists(filepath, testname)
   test fails if a file at filepath is not existing (or accessible by this path)
unittest_arefilecontentsequal(filepath1, filepath2, testname)
   test fails if one of the files do not exist or are not readable or their binary content is different.
unittest_fail(message, testname)
   test fails always with the reason specified in message
   

DATE/TIME FUNCTIONS
timeseconds()
   seconds since 1.1.1970
timemilliseconds()
   milliseconds since last system start
getdatestring()
   get current local date in format d.m.yyyy
gettimestring()
   get current local time in format h:mm:ss
gettimestring(seconds)
   format seconds into format h:mm:ss
getdatetime()
   get current local date and time as structure containing keys: day, month, hour, minute, second
datetoday()
   get date structure with keys day, month, year for current local date
parsedate(text)
   get date structure from date string text e.g. 27.7.2024 or 7/27/2024
dategerman(date)
   get a date string for date structure or date string in german date format e.g. 27.7.2024
dateenglish(date)
   get a date string for date structure or date string in english date format e.g. 7/27/2024
dateweekday(date)
   get week day index number for date structure or date string. 0 is Sunday, 6 is Saturday
isvaliddate(date)
   returns true if date structure or date string is a valid formatted and existing date e.g. 29.2.1967 is not a valid date
daysbetweendates(date1,date2)
   returns number of days between date1 and date2. 0 if date1 is equal to date2. negative if date2 is before date1
isdatebefore(date1,date2)
   returns true if date1 structure or date string is before date2 structure or string
parsetime(text)
   get time structure from time string text e.g. 13:27:05 or 00:59
time2seconds(time)
   calculates seconds since 0:00:00 from a given time structure or time text
seconds2time(seconds)
   calculates time structure from time since 0:00:00
time2shorttext(time)
   calculates mm:ss from a given time structure or time text, if hour is not 0 then result is h:mm:ss
time2text(time)   
   calculates h:mm:ss from a given time structure or time text
   
OUTPUT FUNCTIONS
Hint: all file operations require permission of the application that supports the operation:   r=allow read file,   w=allow write file, i=allow get file information
   
sprintf(format,...)
   format a text that may contain formatting and optional parameters e.g. %03d (similar to C sprintf)
   %% is percent character
   %d integer numbers, %l long numbers, %s string, %c character, %f decimal numbers, %x hexadecimal numbers
printf(format,...)
   print formatted text to standard output (commandline only)
print(text,...)
   print text to standard output (commandline only)
println(text,...)
   print text and newline to standard output (commandline only) 
println()
   print newline to standard output (commandline only) 
printerrorf(format,...)
   print formatted text to standard error (commandline only) 
printerror(format,...)
   print text to standard error (commandline only) 
printerrorln(text,...)
   print text and newline to standard error (commandline only) 
printerrorln()
   print newline to standard error (commandline only) 
fileexists(path)
   check if a file at path really exists
filesize(path)
filesize(file)
   get file size of file at path or of the open file
equalfiles(path1,path2)
   check if content of two files is binary identical
closedfilereadtext(path)
   read whole text from a file at path into one string 
closedfilereadlines(path)
   read all text lines from a file at path into a string vector
closedfilereadbinary(path)
   read whole data from a binary file at path into a string
closedfilewritebinary(path,buffer)
   write binary data buffer into a file at path (overwrite existing file)
closedfilewritetext(path,text)
   write text into a text file at path (overwrite existing file)
closedfilewritelines(path, lines)
   write a vector of lines to a text file
closedfileappendtext(path,text)
   write text into a text file at path (append to existing file)
   
fileopen(filename)
   open binary file for reading only
   returns a file handle of open file or exception when file cannot be opened
   file should be closed using openfileclose(file)
fileopen(filename,mode)
   open file according to mode  
   mode w is write (overwrites file if existing, creates file if not existing)
   mode a is append (creates file if not existing)
   mode r is read 
   returns a file handle of open file or exception when file cannot be opened
   file should be closed using openfileclose(file)
openfileclose(file)
   close an opened file
openfilepos(file)
   get current absolute file position of  an open file
openfilesize(file)
   get number of bytes in an open file
openfileseekbegin(file)
   seek to beginning of an open file
openfileseekbegin(file,pos)
   seek current position to absolute pos of an open file
openfileseekcurrent(file,pos)
   seek current position to relative pos  (can be also 0 or negative) of an open file
openfileseekend(file)
   seek end position of an open file
openfileseekend(file,pos)
   seek current position to a position relative to end (can be also 0 or negative)
openfilechangesize(file,size)
   truncate or extend file size of an open file
openfilereadbinary(file,size)
  read size bytes from an open file beginning at current file position
openfilereadline(file)
  read a line that ends with newline or end of file from an open file beginning at current file position
openfilewritebinary(file,data)
  write binary data to an open file at current file position
openfilewritetext(file,text)
  write text to an open file at current file position (newlines are expanded to \r\n)   
copyfile(destpath, srcpath)
   copy file at srcpath to file at destpath (overwrite existing file), returns true if successful
copybinaryfile(destpath, destwritepos, srcpath, srcpos)
   copy a part of a binary file to a destination binay file at given position
movefile(destpath, srcpath)
   move file at srcpath to file at destpath 
copybinaryfile(destpath, destwritepos, srcpath, srcpos, copysize)
   reads binary part from srcpath and writes it into file destpath at position destwritepos, returns true if successful 
deletefile(path)
   delete file at path if existing  (use system basket)
deletefileforever(path)
   delete file at path if existing  (delete forever without basket)
deletefilelater(path)
   delete file at end of program (usually temporary files)
createfilebackup(srcpath)
   create a backup of src file in current folder with original datetime in name
createfilebackup(srcpath, destfolder)
   create a backup of src file in destfolder with original datetime in name
isbackupfilename(srcpath)
   check if file name has format used by createfilebackup
   
PATH FUNCTIONS

gettemporaryfolder(...)
   get system temporary folder path
   append all specified parameters using joinpath
gettemporaryfilename(nameprefix,ext) 
   get a not existing temporary file name that uses name prefix and file extension e.g. gettemporaryfilename("begin", ".txt")
   the filename will get a random numbering 
gettemporaryfilename(ext) // e.g. gettemporaryfilename(".mid")
   get a not existing temporary file name that uses file extension e.g. gettemporaryfilename(".txt")
   the filename will get a random numbering 
gettemporaryfoldername(nameprefix,ext)
   get a not existing temporary folder name that uses name prefix and file extension e.g. gettemporaryfoldername("begin", ".txt")
   the filename will get a random numbering 
gettemporaryfoldername(ext)
   get a not existing temporary folder name that uses file extension e.g. gettemporaryfoldername(".txt")
   the filename will get a random numbering 
getpersonalfolder(...)
getmydocumentsfolder(...)
   get my documents folder (is same as personal folder)
   append all specified parameters using joinpath 
getprogramfolder(...)
   get program folder 
   append all specified parameters using joinpath 
getapplicationdatafolder(...)
   get application data folder 
   append all specified parameters using joinpath 
joinpath(vector)
   append all vector elements using backslash separator 
joinpath(path,...)
   append all parameters using backslash separator (e.g. joinpath(folder, "subdirectory", "filename.ext")
getfilename(path)
   get file name from path 
   assumes that path is not only a folder path
getdirectory(path)
   get folder part from path without file name
getfileextension(path)
   get the last file extension from the path  (e.g. .txt)
removefileextension(path)
   remove the last file extension from path (e.g. .txt)
quotepath(path)
   add quote character (") before and after the path if the path contains spaces or special characters  
getfullpath(shortpath)
   get a full path for a short path or a relative path   e.g. getfullpath('..\\subdir');
readdirectory(path)
   get the directory entries of a folder as vector of entries
   each entry is a full path of a file or sub folder
createdirectory(path)
   create folder at path
createdirectories(path)
   create folders that build the path
deletedirectory(path)
   delete folder at path
directoryexists(path)
   check if folder exists at path
fileattributes(path)
   get file attributes of path
fileisnewer(path1,path2)
   return true if file at path1 is newer than file at path2 (exception if files do not exist or access to file information is not allowed)
logmessage(text1,...)
   append text to global log file
   all specified arguments are concatenated
   global log file path is set by the application
   
PROFILE FUNCTIONS
getdefaultprofilepath()
   a profile path used by the application e.g. c:\users\yourusername\documents\gnscript.ini, error if no path available
writeprofilestring(inipath,section,key,value)
   set .ini entry key inside section to value in file inipath
getprofilestring(inipath,section,key,defaultvalue)
   get .ini value of entry key inside section in file inipath
   if value is not set then use defaultvalue
getprofilestring(inipath,section,key)
   get .ini value of entry key inside section in file inipath
   if value is not set then use empty string
writeprofilebool(inipath,section,key,value)
   set .ini entry key inside section to boolean value in file inipath
getprofilebool(inipath,section,key,defaultvalue)
   get .ini boolean value of entry key inside section in file inipath
   if value is not set then use defaultvalue
getprofilebool(inipath,section,key)
   get .ini value of entry key inside section in file inipath
   if value is not set then use false
   
MATHEMATICAL FUNCTIONS

odd(value)
   check if value is odd (1,3,5,7..)
round(value)
   round value to next integer number (for fractional part 0.5-0.999 round up, else round down)
round(value,digits)
   round value to next decimal number that has number of digits in fractional part 
random(maxvalue)
   get a random value between 0 and maxvalue-1
random()
   get a random value between 0 and 65535
startrandom(initrandom)
   by default random() generates random numbers. Use startrandom(constantnumber) to generate (pseudo random) number list during testing (e.g. startrandom(0); random(1000000) produces 38). For unit tests random results are not so easy to verify.
abs(x)
   -x if x is a negative number
min(vector)
   minimum of all values in the vector
min(x,y,...)
   minimum value of all arguments
   if parameter is a vector then also all the vector element values are used too
   e.g. min(7, [4,5,6]) results in 4
max(vector)
   maximum of all values in the vector
max(x,y,...)
   maximum value of all arguments
   if parameter is a vector then also all the vector element values are used too
   e.g. max(3, [4,5,6]) results in 6
sum(x,y,...)
   sum value of all arguments
average(x,y,...)
   average value of all arguments
swap(&x,&y)
   swap content of two variables 
acos(x)
   arcus cosinus value of x
asin(x)
   arcus sinus value of x
atan(x)
   arcus tangens value of x
atan2(x,y)
   angle between ray to (x,y) and positive x-axis
cos(x)
   cosinus of value x
cosh(x)
   hyperbolic cosinus of value x
exp(x)
   base (constant e = 2.718) exponented by value x 
log(x)
   logarithm of value x using base e (2.718)
log10(x)
   logarithm of value x using base 10
sin(x)
   sinus of value x
sinh(x)
   hyperbolic sinus of value x
tan(x)
   tangens of value x
tanh(x)
   hyperbolic tangens of value x
sqrt(x)
   square root of positive value x
sqr(x)
   x * x
ceil(x)
   rounding up to next higher integer number
floor(x)
   rounding down to next lower integer number
fract(x) 
   part behind dot of value x
div(x,y)
   integer division result of x / y
modf(x)
   fractional part of value x
fmod(x,y)
   floating point remainder of x / y
pow(x,y)
   x exponented by value y  (pow(2,3) = 8)


BINARY BUFFER FUNCTIONS   

buffergetbyte(&buffer,pos)
   get a single byte value inside buffer at byte position pos
buffersetbyte(&buffer,pos,value)
   set a single byte to value inside buffer at byte position pos
bufferfillbytes(&buffer,pos,len,value)
   set next len bytes to same value inside buffer at position pos

DIALOG FUNCTIONS

messagebox(text, caption, boxtype)
   show a message box with caption and text
   box type can be: error, warning, information, ok, okcancel, yesno, yesnocancel, question, exclamation , abortretryignore, retrycancel or a number 
   message box returns answers:  yes, no, ok, cancel, retry, abort, ignore or a number
messagebox(text, caption)
   show an OK message box with caption 
messagebox(text)
   show an OK message box with caption Information
getinputlinebox(prompt, caption, &inputtext)
   show a message box with an edit text box and ok and cancel button. Returns 1 for ok and 0 for cancel and sets inputtext to the user input.
filechooser(initpath, caption, options, defaultext, filter) 
   show a file chooser dialog for selecting a file or folder using caption and optional initial path
   options can be a text list: save, open, multi, pickfolders, overwriteprompt, pathmustexist, filemustexist
   default extension defaultext is added automatically when no file extension was specified in the dialog
   filter is a list of pairs of caption|fileextensions  e.g. Images|*.gif;*.png;*.jpg|All files (*.*)|*.*|| 
   filter must end with ||
   filter fileextensions must be separated by ;
   returns selected path 
   returns null when nothing is selected
   when using multi option a vector of path is returned

PROCESS FUNCTIONS
findapplication(appname)
   get the full path of an application if it is found in path, empty string if it is not found
shellexecutecommandline(cmdline)
shellexecutecommand(cmdline)
   start a command line e.g. shellexecutecommandline("notepad2 mytextfile.txt"), it may also contain a DOS command e.g. del /S /Q *.bak
shellexecute(prog)
shellexecute(prog, params)
   open prog with optional params in a system shell e.g. shellexecute("notepad", "file.txt");
runprocess(cmdline)
   execute a commandline process, returns exit code   (0 is successful)
runprocess(cmdline,&processoutput)
   execute a commandline process and store the stdout text from process in processoutput variable, return exit code (0 is successful)

SOUND FUNCTIONS
   
beep()
   simple system beep sound
beep(soundname)
   sound names are simple,ok,warning,information,error,question or a number of a system beep sound

playmidi(events,...)
   play midi events (e.g. playmidi('144 60 127');sleep(500);playmidi('144 60 0'); plays a C note for half second

saytext(text)
saytext(text,options)
   speaks text using system speech (depending on installed text to speech driver it can be language dependent or poor quality)
   options can be 'usespeechsynth' or 'usesapi' (default) 

CLIPBOARD FUNCTIONS
setclipboardtext(text)
   puts the text into Windows clipboard
getclipboardtext()
   gets current Windows clipboard text (empty string if not available)
   
GRAPHIC FUNCTIONS (still experimental)

picturecreate(width,height)
   create an empty picture with width x height pixels
picturedestroy(picture)
   free the picture 
pictureload(filename)
   load a picture from existing file (can be .bmp, .png, .gif, .jpg, .tif)
picturesave(picture, filename, format)
picturesave(picture, filename)
   save picture to a file (overwrites existing file) 
   format can be "BPM", "TIFF", "JPEG", "GIF", "PNG" or empty or omitted
   if no format is given then file format is chosen from file extension (.bmp, .tif, .jpg, .gif or .png) or default format is "BPM".
 picturesavebpm(picture, filename)
   save picture to a .bpm file
picturefillrect(picture,x,y,width,height,color)
   fill a rectangle part of a picture with a solid color
picturedrawtext(picture,x,y,width,height,text,color)
   draw a colored text into a rectangular area of a picture
getcolor32(alpha,r,g,b)
   get an rgb color with alpha channel (transparency level, 0= not transparent till 255=full transparent)
getcolor24(r,g,b)
   get an rgb color that is not transparent
getred(color)
   get red part of rgb color
getgreen(color)
   get green part of rgb color
getblue(color)
   get blue part of rgb color
getalpha(color)
   get alpha part of transparent rgb color

LICENSE FUNCTIONS
getlicensestate()
   check if a valid license is installed (alternative a demo mode could run if supported by the script)
haslicensefeature(featurename)
   check if license supports an extra feature by name (e.g. use of certain operations)

GNSCRIPT DEBUGGER

gnscript contains an embedded debugger when started with -d (-debug) option

gnscript -d filename.script

By default a Windows debugger dialog appears when the running stops at a breakable line or exception or breakpoint. By default it displays the results to a debugger window. The setting useconsole can be set to true to display the results in console.

With setting usegnscriptdebugger=false the command line debugger can be used where the debugger can be directed using text input commands that must be finished with a return key. 

The debugger stops at first source code line  and shows the line.
It waits for user input.

help,h       shows possible input commands
return,r     continue till end of function
list,l       show a list of source code lines at a given line number
continue,c   continue till a breakpoint stops or an exception occurs
quit,q       exit script 
stepinto,s   execute next command and if it is a function call step into the function
stepover,n   execute next command and if it is a function call step over the function
breakpoint,b set a manual a conditional breakpoint at a line
variable,v   set a variable change breakpoint
print,p      evaluate a script line and print the result on console



GNSCRIPT PROFILER

gnscript contains an embedded profiler when started with -profile=sortmethod
The profiler counts every code line that was executed and measures the time that the execution tooks.
The results are written to gnscript.log at end of script.

sortmethod are: count, sumbrutto, sumnetto, singlebrutto, singlenetto  (default: sumbrutto)
sortmethod can start with + (ascending) or - (descending)  to change the sort order (default: -, descending)

This can help to optimize a slow script to find where the most time of the script is lost.

Hint: in many cases slow scripts are caused by unnecessary copying of function arguments. use reference parameters (&) if possible.

Hint: if a script runs very very slow look also into process task list if unnecessary processes are running (e.g. old instances of gnscript).

GNSCRIPT LIMITATIONS

function recursive calls are limited to a number specified by the application to prevent from possible endless recursive calls (default: 100). Allowing too many recursive calls can lead to a application stack overflow.

for, while, until loops counts are limited to a number specified by the application to prevent from possible endless loop (default: 10000).

using prefix and postfix operators in same expression are not allowed (postfix operator creates a r-value result that cannot be modified)

GNSCRIPT INTERPRETER

   gnscript script "var x = 1; ++++x;"
interpreter result:
3

executes the script and displays the result value (converted to string) or error exception if an error occured

GNSCRIPT SCANNER

    gnscript -scanner script "var x = 1; ++++x;"

scanner result:
console L1C2 VARIABLE var
console L1C6 IDENTIFIER x
console L1C8 =
console L1C10 NUMBER 1
console L1C11 ;
console L1C13 ++
console L1C15 ++
console L1C17 IDENTIFIER x
console L1C18 ;

displays the scanner result token list with their source code positions  or a syntax error exception if an invalid token was found

GNSCRIPT PARSER

    gnscript -parser script "var x = 1; ++++x;"

parser result:
STATEMENTS{
  VAR  x
    CONSTVALUE 1
  ASSIGNMENT +=
    ASSIGNMENT +=
      VARIABLE x
      CONSTVALUE 1
    CONSTVALUE 1
}
displays the parser result recursive grammar structure or an error exception if a syntax error was found

EXTENDING GNSCRIPT LANGUAGE 

An application that uses gnscript library can override class GNScriptContext to extend the language.

CONSTANTS
    own constants can be defined for own purposes e.g. EVENTTYPE_TEXT  to string value 'T'
    
FUNCTIONS
    the available system functions list can be extended by own functions. The own functions also support variable parameters list and return an object value (can also be a vector or structure or null) and error message if the call fails for any reason (e.g. parameters are not valid).
    e.g.  setStatusbarText(shortmsg)

LIVE VARIABLES 
    the application may extend the language by new object type variables. Basic types can be overrided to quicklier define own structures.
    By default the live variables are used by reference. A clock variable could be defined and each access to the variable delivers current time.
    Live variables help to bind gnscript objects to existing data structures in the application. The original data is not duplicated in the script language. The original data is accessed during object usage  e.g. song.event[i] = 
    
    based string objects     getter and setter for string value can be defined
    number objects           getter and setter for numeric value can be defined
    structure                field names can be defined and getter and setter for each member can be defined, remove and insert key can be defined
    vector                   getter and setter for each indexed key can be defined, remove and insert index can be defined
    

LIBRARIES

a DLL can implement a library extension.

The main application (interpreter) converts calls to a library extensions to calls to dll functions. Objects are converted to global data and on dll side automatically converted back. The DLL can create own object types by implementing base classes.
Such objects use the derived flag. These results objects are not copied. The interpreter remembers the class instance on the DLL side and does not hold any data copies. Everytime the DLL library must be asked and the DLL implementation can react on changes (e.g. a vector object in the library object is not visible to the interpreter but gives functions to access the vector e.g. ask vector size, ask existing vector element, pop, push, and when the interpreter does not use the object anymore it automatically calls a dll function that destroys the created object instance.





GNSCRIPT UNIT TEST EXAMPLE 

==== content of file goto.gnscript ==============================================================================
function testjumpforward()
{
   var x='test'; 
   goto forwardsamelevel;
   x = 'wrong';
   return x;
   forwardsamelevel:
     x = 'correct';
     return x;
}

function testjumpbackward()
{
   var x='test'; 
   goto forwardsamelevel;
   return x;
   
   backwardsamelevel:
     x = 'correct';
     return x;

   forwardsamelevel:
      goto backwardsamelevel;
   x = 'wrong';
   return x;
}

function testjumpupward()
{
   var level = 'test';
   
   {
      var level = 'top'; 
      
      {{
          var level = 'middle';
          
          {{
              var level = 'deep';
             
          
              goto jumpupward;
          }}
          
          jumpupward:
          
          if (level == 'middle')
              return 'correct';
      }}
      
   }
   return 'wrong';
}

function testwronggoto()
{
    try {
       goto nowhere;
    }
    catch(var e) {
        return trim(e).substitute("^.*\\\\", "");
    }
}

function testfunctionwithlabel()
{
   print("before");
   labelinfunction: 
   print("after");
}

function testgotointofunction()
{
    try {
       goto labelinfunction;
    }
    catch(var e) {
        return trim(e).substitute("^.*\\\\", "");
    }
}

function testjumpdownward()
{
   try {
     var level = 'test';
     
     goto downward;
     
     level = 'top';
     {{{{
     var newvariables= 'new';
     downward:
         return 'wrong';
     }}}}
   }
   catch(var e)
   {
     return e.substitute("^.*\\\\", ""); // warning: the pattern must be ^.*\\  and since it is in a literal we must write it "^.*\\\\"
   }
   return level;
}

function testdoublelabel()
{
   try {
     var level = 'test';
     goto doublelabel;
     
     level = 'afterfirstgoto';
     
     doublelabel:
     level = 'firstgoto';
     return 'wrong1';
     
     doublelabel:
     level = 'secondgoto';
     return 'wrong2';
   }
   catch(var e)
   {
     return e.substitute("^.*\\\\", "");
   }
}

function testgotoloopforever()
{
  var n = 0;
  try {
     start:
       n++;
     goto start;
  }
  catch(var e)
  {
     return e.substitute("^.*\\\\", "");
  }
}

unittest_areequal(testjumpforward(),'correct', "forward jump");
unittest_areequal(testjumpbackward(), 'correct', "backward jump");
unittest_areequal(testjumpupward(), 'correct', "upward jump");
unittest_areequal(testwronggoto(),'goto label not found: nowhere',"goto label not found");
unittest_areequal(testgotointofunction(), 'goto to label not allowed: labelinfunction', "goto into function");
unittest_areequal(testjumpdownward(), 'goto to label not allowed: downward', "goto label downward");
unittest_areequal(testdoublelabel(), 'label is defined twice in same scope: doublelabel', "label used twice");
unittest_areequal(testgotoloopforever(),'goto loop never ending',"goto loop forever");

unittest_areequal(unittest_count(), 8, "incomplete");
print(unittest_results());


==========================================================================================



no output if all tests successful (exit code 0)
error lines if any test fails
e.g.
unit test incomplete failed:  result=7  correct=8

testall.gnscript
   starts internal unit tests and all tests in subfolders
   fullprograms contains many example files that test itself with unit tests
   unit test 100-createindex.gnscript reads all the gnscript examples and creates index.txt that shows which gnscript features (e.g. functions, operators) are used in the example scripts

==========================================
GNSCRIPT USAGE
==========================================
gnscript 1.0 by G. Nagler (c) 2021 GN Midi Solutions
usage:
gnscript [options] -tests=[testnamefilter]
gnscript [options] -script text
gnscript [options] filename [params]
options:
-source        show source code
-lines         show all breakable lines
-scan          show scanner token list
-parse         show parser tree
-check         check if parser errors occur
-debug         use console debugger
-lang=xx       use language translations (en, de) for translatable text
-profile=xxx   print times and calling counts sorted ascending or descending
               by method +xxx or -xxx
               xxx can be: count,sumbrutto,sumnetto,singlebrutto,singlenetto


example command lines:
   > gnscript example.gnscript
   
   > gnscript -script "3+pow(2,3)"
   11
   
   > gnscript -d -script "function x(n) { return pow(2,n); } return 3+x(3)"
   at console L001C036 return 3+x(3)
   >> s
   at console L001C045 x(3)
   >> s
   at console L001C017 return pow(2,n)
   >> s
   at console L001C024 pow(2,n)
   >> s
   11
   

   > gnscript -tests
   951 unit tests successful
   
   > gnscript -d example.gnscript
   
   > gnscript -profile example.gnscript
   
   > gnscript -script "var l='gnscriptdbhash';uselibrary(l); l.getlibrarysummary()'"
   


==========================================
BUG REPORT
==========================================
Please create a small .gnscript example that contains a unit test that fails on your side. It should make clear what result you expected.

example bug report in file example_problem.gnscript:

unittest_areequal(wrongfunction('x','y'), "expected result", __CODEPOS__() );

unittest_areequal(unittest_count(), 1, "incomplete");
print(unittest_results());

Please send a bug report by e-mail to info@gnmidi.com

================================================================
TODO 
================================================================
* assignment of a variable could be l-value copy => any modification of original variable could be a problem e.g. var a = b; b++; => a still original b
* not sure if ((string)'0001.0' + 1)+1   should return 0001.011  or 2.01 or 2.010000 or error
* not sure if ((string)'0001.0' + 1) * 2 should return 2.020000 or error
* + operator for inserting values into a vector e.g. [3,4]+5 == [3,4,5]
* + operator for combining vectors e.g. [3,4]+[4,5] == [3,4,4,5]
* + operator for combining structures e.g. {a:3}+{b:4}  == {a:3, b:4}
* == and != compares for vectors and structures  (currently it works but converts to string and identical numbers could different  e.g. [3.0] != [3])
* switch case with numbers e.g. 3.1  or 0007 if switch condition is not casted  might be not well defined
