Manual: Difference between revisions

From NEOSYS Dev Wiki
Jump to navigationJump to search
No edit summary
 
(247 intermediate revisions by 3 users not shown)
Line 1: Line 1:
=== Hello World ===
=== Complete list of var, dim and exoprog functions ===


After installing Exodus, you can develop Exodus programs from any operating system command line.
[[Functions]]


Any programs you develop are stored in the same directory as their source code. They are also copied to an folder in your home directory - Exodus on Windows and bin or lib on other operating systems. These folders are added to the end of your path by the Exodus installer. This means that, as long as you are logged in as the developing user, and there is no similarly named program early on in your path (e.g. operating system commands) you can use any developed program from any command line. To make any programs available to other users you must arrange for the programs to be copied to some directory on their path.
=== ICONV/OCONV PATTERNS ===


==== Edit ====
==== Decimal (MD/MC) ====


Use Exodus's all-in-one editor+compiler+cataloger 'edic'
{|class="wikitable"
!input!!conversion (string)!!output
|-
|1234||MD2||12.34
|-
|1234||MD20||1234.00
|-
|1234||MD20,||1,234.00
|-
|1234.5678||MD2||12.35
|-
|1234.5678||MD20||1234.57
|-
|1234.5678||MD20,||1,234.57
|-
|1234||MC2||12,34
|-
|1234||MC20||1234,00
|-
|1234||MC20,||1.234,00
|-
|1234||MD20-||1234.00
|-
|-1234||MD20-||1234.00-
|}


edic hello1
==== Date (D) ====


edic will give you a skeleton Exodus program which you can develop as you wish.
{|class="wikitable"
!input!!conversion (string)!!output
|-
|12345||D||18 OCT 2001
|-
|12345||D/||10/18/2001
|-
|12345||D-||10-18-2001
|-
|12345||D2||18 OCT 01
|-
|12345||D/E||18/10/2001
|-
|12345||DS||2001 OCT 18
|-
|12345||DS/||2001/10/18
|-
|12345||DM||10
|-
|12345||DMA||OCTOBER
|-
|12345||DY||2001
|-
|12345||DY2||01
|-
|12345||DD||18
|-
|12345||DW||4
|-
|12345||DWA||THURSDAY
|-
|12345||DQ||4
|-
|12345||DJ||291
|-
|12345||DL||31
|}


There must be one and only one "function main()" statement and this is run when the program is started.
==== Time (MT) ====


By convention, main() must return an integer value. If desired, this can be used to indicate "success" with zero or "failure" with some error number.
{|class="wikitable"
!input!!conversion (string)!!output
|-
|234800||MT||17:13
|-
|234800||MTH||05:13PM
|-
|234800||MTS||17:13:20
|-
|234800||MTHS||05:13:20PM
|-
|0||MT||00:00
|-
|0||MTH||12:00AM
|-
|0||MTS||00:00:00
|-
|0||MTHS||12:00:00AM
|}


The programinit() and programexit() lines are required and provide all the standard multivalue system variables using a simple C++ class macro.
==== Hex (HEX/MX) ====


<pre>
{|class="wikitable"
#include <exodus/program.h>
!input!!conversion (string)!!output
|-
|ab||HEX||(same as HEX8 or HEX4 depending on platform)
|-
|ab||HEX8||0000006100000062
|-
|ab||HEX4||00610062
|-
|ab||HEX2||6162
|-
|15||MX||F
|-
|254||MX||FE
|-
|255||MX||FF
|-
|256||MX||100
|-
|27354234||MX||1A1647A
|}


programinit()
==== Text (L/R/T) ====


function main() {
{|class="wikitable"
        printl("hello1 says 'Hello World!'");
!input!!conversion!! (string)output
        return 0;
|-
}
|abcd||L#3||abc
|-
|ab||L#3||ab&#9251;
|-
|abcd||R#3||bcd
|-
|ab||R#3|| &#9251;ab
|-
|ab||T#3||ab&#9251;
|-
|abcd||T#3||abc&trade;d&#9251;&#9251; 
|-
|42||L(0)#5||42000
|-
|42||R(0)#5||00042
|-
|42||T(0)#5||42000
|}


programexit()
=== Dictionaries ===
</pre>


==== Save and compile ====
Exodus dictionaries enable classic multivalue database data definition. Dictionaries are just normal Exodus multivalue files that contain one record for each data column definition. You can use Exodus's edir program to manually edit dictionaries.


Make any changes you want in to the skeleton and save it.
Dictionary file names must start with the word "dict_". For example, if you have a "books" file, then you will probably have a "dict_books" file.


NB If you just want to try out the skeleton program as is then you must still explicitly save it otherwise if you just exit without saving,  edic will assume you have changed your mind and that no longer want the hello1 program, and will cancel. For the default editor (nano), explicit save is usually Ctrl+O.
You can list the contents of a dictionary by typing "list dict_filename".


On saving hello1, edic will compile and catalog it.
==== Exodus Dictionary Format ====


==== Run ====
{|
|-
|0 || DICTID || Field/Column Code
|-
|1 || DICTTYPE || "F" or "S" : "F" means use Field No (i.e. raw data) and "S" means use Source Code (i.e. a function).
|-
|2 || FIELDNO || Field number (0=key, 1=field 1 etc for "Fields"
|-
|3 || TITLE ||Title on reports
|-
|4 ||SM || S or M or Mnn : "Single Value" or "Multivalue" or "Multivalue Group nn"
|-
|5 ||KEYPARTNO || Multipart keys are separated by * characters.
|-
|6 ||
|-
|7 || CONVERSION ||Conversion (MD/MT/D etc.)
|-
|8 || SOURCE || Source Code of a subroutine to calculate the field. Multivalues are lines and the result must be placed in a variable "ANS".
|-
|9 || JUST || "L" or "R" or "T" requesting left, right or text justification
|-
|10||WIDTH||Column Width on fixed width reports
|}


To run/open/execute your new hello1 program just type its name.
==== Sort/Select Command ====


hello1
Exodus provides the classic multivalue sort/select command within any Exodus program followed by readnext().


and the output is ...
Classic multivalue select/readnext functions only provide the keys of the selected records. Exodus provides the classic select/readnext and also selectrecords/readnextrecord which provides complete records instead of just keys.


hello1 says 'Hello World!'
The format of the select/sselect command is as follows:


=== Local subroutines ===
<PRE>
SELECT|SSELECT


To simulate classic multivalue basic's "gosub/return" in Exodus, you can add additional subroutines and functions above or below your "main" function.
{max_number_of_records}


Local subroutines and functions (including the "main" function) can be any order. There is no rule that functions must appear before or above the code that calls them.
{using filename}


==== Simple Example ====
filename


Here is hello1 modified to call a subroutine that says something.
{datakeyvalue} ...


The word "gosub" is just there for classical clarity. It could be omitted. It could also be replaced by "call" which is also a throwaway word. While gosub and call are interchangeable, if you are going to use them at all, it is probably a good idea to use "gosub funcx()" to indicate internal subroutines and "call funcx()" to indicate external subroutines.
{BY|BY-DSND fieldname} ...
{


All functions/subroutines must fall between the programinit() and programexit() lines, and cannot be nested otherwise compilation will fail.
  WITH


<pre>
  {NO|ALL|ANY}
#include <exodus/program.h>


programinit()
  dict_field_id


function main() {
  {
        printl("hello1 says 'Hello World!'");
  CONTAINING|STARTING|ENDING|LIKE|EQ|NE|NOT|GT|LT|GE|LE=|<>|>|<|>=|<= value(s)
  |
  BETWEEN value AND value
  }


        gosub subr1();
  {AND|OR}


        return 0;
} ...
}
 
subroutine subr1() {
        printl("subr1 says 'Hello'");
}
 
programexit()
</pre>
</pre>


output:
=== Functions and Commands ===
 
hello1 says 'Hello World!'
subr1 says 'Hello'
 
=== Subroutine and Function Parameters (Arguments) ===
 
Parameter names must be prefixed by "in", "out" or "io" to indicate if they are inbound, outbound or both.
 
Inbound parameters (prefixed with "in") cannot be amended within the function and the compiler will enforce this. This provides a guarantee to the programmer calling the subroutine or functions that their inbound parameters will not be modified by calling the function. Inbound parameters may be provided by the calling program either as a variable or as raw data like "xyz" or 123 etc.
 
In/Outbound parameters ("io") must be provided by the calling program in the form of a variable and not raw data. The function can both read and write into these parameters.
 
Outbound parameters ("out") are indicate to the programmer using the function how it works. Outbound parameters are actually no different from in/outbound parameters. There is no simple way to enforce "write only" parameters using the underlying C++ language.
 
All the usual C++ parameters are also allowed for example "int arg1" or "const char* arg1" etc. This might be useful in special cases or where performance is critical.
 
=== External functions and subroutines ===
 
Editing and compiling external subroutines and functions in Exodus is identical to normal programs except:
 
#the word "program" becomes "library" so we have "library.h", "libraryinit()" and "libraryexit()".
#function main can have any parameters you like, for example: "function main(in arg1, in arg2, out arg3)
 
NB The name of the function internally must be main and NOT the name of the function so we still have "function main(...)" but this time it can have parameter names.
 
==== Example func1 ====
 
edic func1
 
<pre>
#include <exodus/library.h>
 
libraryinit()
 
function main(in arg1, out arg2) {
        printl("func1 says 'Hello World!'");
        arg2="arg1 was "^arg1;
        return 999;
}
 
libraryexit()
</pre>
 
==== Example prog1 using func1 ====
 
Compiling func1 created a func1.h file that is required to "glue" the func1 into any program that wants to use func1.
 
You just need to insert an #include "func1.h" somewhere between "programinit()" and "programexit()" and not within any function or subroutine.
 
edic prog1
 
<pre>
#include <exodus/program.h>
 
programinit()
 
//you must "declare" that we want to use func1
#include "func1.h"
 
function main() {
        printl("prog1 says 'Hello World!'");
 
        //we print what func1 returns, and also what func1 returns in arg2
        printl(  func1("myarg1",arg2)  );
        printl(arg2);
 
        return 0;
}
 
programexit()
</pre>


=== Functions versus Subroutines ===
==== String Commands ====


Functions can be used anywhere an expression would be used whereas subroutines can only be used as statements.
Most string functions like trim() that return a new modified string have a corresponding modify in place command like function like trimmer() that is is usually much faster.
So we have convert and converter, replace and replacer, insert and inserter and so on.  


The same principle applies to both internal and external subroutines/functions.
Therefore by preference use


Both subroutines and functions can return information in any of their "out" or "io" arguments.
trimmer(v1);
// or
v1.trimmer()


The word "call" or "gosub" before the use of a function or subroutine is optional and can be omitted.
instead of


==== Functions ====
v1 = trim(v1);
// or
v1 = v1.trim();


Functions return a value so the calling program can use a function anywhere an expression is required, for example on the right hand side of an assignment.
==== Function Types ====


var yy=funcx(xx);
{|class="wikitable"
!TYPE !!FUNCTION TYPE||
|-
|var= ||traditional functions that return values and can be used in expressions and be on the right hand side of assignments||
|-
|if ||traditional conditional statements that started with "if" or ended with "then/else" (or could have)||
|-
|cmd ||traditional commands with no outputs||
|-
|expr ||traditional commands that now have outputs and can be used in expressions||
|}


Even though a function returns a value, it can be ignored.
==== Parameters/Argument Types ====


call funcx(xx);
{|class="wikitable"
gosub funcx(xx);
|in|| Parameters that provide data to the function. Can be variables or raw data like 1 or "X"||
funcx(xx);
|-
|unspecified||Same as "in". Omission of the most common type de-clutters the documentation. NB When defining your own subroutines and functions "in" cannot be omitted from the source code.||
|-
|io|| Parameters that may provide and/or return data. Must be variables. Cannot be raw data like 1 or "X"||
|-
|out|| Parameters that return data. Must be variables. Cannot be raw data like 1 or "X"||
|-
|}


==== Subroutines ====
Optional Parameters


Subroutines do not return a value so they can only be used as statements.
{|class="wikitable"
!Key !!Default||
|-
|= "" ||""||
|-
|= " " ||" "||
|-
|= "." ||"."||
|-
|= 1 ||1 ||
|-
|= 0 ||0||
|-
|= true ||true||
|-
|= false ||false||
|}


call subrx(xx); 
==== Field mark characters ====
gosub subrx(xx); 
subrx(xx);       
var yy=subrx(xx); //will not compile


=== Differences between Exodus's Internal Subroutines and Functions and those of classic multivalue basic ===
Exodus implements a PICK OS data structure called a "dynamic array". This is simply any string which uses six specific unprintable ASCII delimiter characters (\x1A to \x1F) to separate its various parts. The parts are referred to as records, fields, values, subvalues, text, and subtext and fall within each other.


Exodus allows some classic multivalue basic external subroutines and functions to be implemented as local subroutines/functions thereby allowing you to keep a programs source all in one file where desired. This is a major improvement in source code management. You can still make external subroutines and functions where that is the best strategy, for example to provide some feature to more than one program.
Dynamic arrays therefore implement sparse six dimensional arrays.


In classic multivalue basic, if you wanted to implement some function to be used in any expression, or you wanted to call a subroutine with parameters, or you wanted to be sure that a subroutine would not accidentally overright main program variables, you had to implement it as an external subroutine or function. Therefore a classic multivalue program would sometimes end up as a set of separately compiled source code even when the best thing would be to keep it all together in one source file.
Typical CPU caching architecture favours similar values being adjacent in memory therefore implementing them as strings of values separated by delimiter characters can have performance advantages over more complex structures.


Internal subroutines and functions have access to the global variables of the main program so you might want them to make them external to make sure that there is no risk that they touch any global variable.
In practice the vast majority of dynamic arrays consist of "fields" separated by the FM character (\x1E) but it is very common for fields to have values separated by the VM character (\x1D) and values to have subvalues using SM (\x1D).


Exodus's local subroutines and functions are rather different and arguably much better than classic multivalue local subroutines since they have their own set of variables.
Since the six delimiter characters fall in the unprintable character range certain other characters have been designated as usable for coding and printing. For example the FM character is represented as ^ and can be entered in source code appended with _var to indicate that the string must be converted to internal format.


#can be called with parameters e.g. gosub funcx(xx,yy,zz)
var v1 = "f1^f2^f3"_var; // Three fields
#except for program global variables all variables are private (preventing many bugs)
#can return a result e.g. abc=funcx(xx,yy,zz)


Essentially they are identical to *external* functions and subroutines except that 1) they are written in the main program and 2) they have access to the programs global variables.
var v2 = "f1^v1]v2]v3^f3"_var; // Three fields, 2nd field has 3 values. It is "multivalued".


They do not have any access to the variables of the calling program unless they are passed as parameters or are defined as global variables.
Anything which contains a collection of fields can be considered as a "record" and records can be stored in files with a unique primary key. The fields might represent different columns of a traditional database table. So anonymous field number 1 might be a contact name, field 2 the contact address, field 3 a multivalued list of contact points. etc.


They only have one entry point whereas in classic multivalue basic you can jump into the middle of any local subroutine. To simulate this type of coding in in Exodus you must create nested subroutines or functions.
{|class="wikitable"
!Delimiter<br>name!!var||Hex!!Display!!cstr " "||char ' '
|-
|Record Mark  ||RM||\x1F|| ` || _RM || RM_
|-
|Field Mark    ||FM||\x1E|| ^ || _FM || FM_
|-
|Value Mark    ||VM||\x1D|| ] || _VM || VM_
|-
|Subvalue Mark ||SM||\x1C|| } || _SM || SM_
|-
|Text Mark||TM ||\x1B|| &#124; || _TM || TM_
|-
|Subtext Mark  ||STM||\x1A|| ~ || _STM || STM_
|}


The old RETURN TO XYZ syntax is not supported at all and such code must be redesigned to eliminate it.
{|class="wikitable" style="text-align: center;"
|-
|align=right|<b> Delimiter Name :</b>  ||Record Mark||Field Mark||Value Mark||Subvalue Mark||Text Mark||Subtext Mark
|-
|align=right|<b>var :</b>    || RM ||FM  ||VM  ||SM  ||TM  ||STM
|-
|align=right|<b>Display :</b>||`  ||^  || ]  || }  || &#124; || ~
|-
|align=right|<b>Hex :</b>    ||\x1F||\x1E||\x1D||\x1C||\x1B||\x1A
|-
|align=right|<b>cstr " " :</b>  ||_RM ||_FM ||_VM ||_SM ||_TM ||_STM
|-
|align=right|<b>char ' ' :</b>  ||RM_ ||FM_ ||VM_ ||SM_ ||TM_ ||STM_
|}

Latest revision as of 07:23, 1 April 2025

Complete list of var, dim and exoprog functions

Functions

ICONV/OCONV PATTERNS

Decimal (MD/MC)

input conversion (string) output
1234 MD2 12.34
1234 MD20 1234.00
1234 MD20, 1,234.00
1234.5678 MD2 12.35
1234.5678 MD20 1234.57
1234.5678 MD20, 1,234.57
1234 MC2 12,34
1234 MC20 1234,00
1234 MC20, 1.234,00
1234 MD20- 1234.00

Date (D)

input conversion (string) output
12345 D 18 OCT 2001
12345 D/ 10/18/2001
12345 D- 10-18-2001
12345 D2 18 OCT 01
12345 D/E 18/10/2001
12345 DS 2001 OCT 18
12345 DS/ 2001/10/18
12345 DM 10
12345 DMA OCTOBER
12345 DY 2001
12345 DY2 01
12345 DD 18
12345 DW 4
12345 DWA THURSDAY
12345 DQ 4
12345 DJ 291
12345 DL 31

Time (MT)

input conversion (string) output
234800 MT 17:13
234800 MTH 05:13PM
234800 MTS 17:13:20
234800 MTHS 05:13:20PM
0 MT 00:00
0 MTH 12:00AM
0 MTS 00:00:00
0 MTHS 12:00:00AM

Hex (HEX/MX)

input conversion (string) output
ab HEX (same as HEX8 or HEX4 depending on platform)
ab HEX8 0000006100000062
ab HEX4 00610062
ab HEX2 6162
15 MX F
254 MX FE
255 MX FF
256 MX 100
27354234 MX 1A1647A

Text (L/R/T)

input conversion (string)output
abcd L#3 abc
ab L#3 ab␣
abcd R#3 bcd
ab R#3 ␣ab
ab T#3 ab␣
abcd T#3 abc™d␣␣
42 L(0)#5 42000
42 R(0)#5 00042
42 T(0)#5 42000

Dictionaries

Exodus dictionaries enable classic multivalue database data definition. Dictionaries are just normal Exodus multivalue files that contain one record for each data column definition. You can use Exodus's edir program to manually edit dictionaries.

Dictionary file names must start with the word "dict_". For example, if you have a "books" file, then you will probably have a "dict_books" file.

You can list the contents of a dictionary by typing "list dict_filename".

Exodus Dictionary Format

0 DICTID Field/Column Code
1 DICTTYPE "F" or "S" : "F" means use Field No (i.e. raw data) and "S" means use Source Code (i.e. a function).
2 FIELDNO Field number (0=key, 1=field 1 etc for "Fields"
3 TITLE Title on reports
4 SM S or M or Mnn : "Single Value" or "Multivalue" or "Multivalue Group nn"
5 KEYPARTNO Multipart keys are separated by * characters.
6
7 CONVERSION Conversion (MD/MT/D etc.)
8 SOURCE Source Code of a subroutine to calculate the field. Multivalues are lines and the result must be placed in a variable "ANS".
9 JUST "L" or "R" or "T" requesting left, right or text justification
10 WIDTH Column Width on fixed width reports

Sort/Select Command

Exodus provides the classic multivalue sort/select command within any Exodus program followed by readnext().

Classic multivalue select/readnext functions only provide the keys of the selected records. Exodus provides the classic select/readnext and also selectrecords/readnextrecord which provides complete records instead of just keys.

The format of the select/sselect command is as follows:

 SELECT|SSELECT

 {max_number_of_records}

 {using filename}

 filename

 {datakeyvalue} ...

 {BY|BY-DSND fieldname} ...
 
 {

  WITH

  {NO|ALL|ANY}

  dict_field_id

  {
   CONTAINING|STARTING|ENDING|LIKE|EQ|NE|NOT|GT|LT|GE|LE=|<>|>|<|>=|<= value(s)
   |
   BETWEEN value AND value
  }

  {AND|OR}

 } ...

Functions and Commands

String Commands

Most string functions like trim() that return a new modified string have a corresponding modify in place command like function like trimmer() that is is usually much faster. So we have convert and converter, replace and replacer, insert and inserter and so on.

Therefore by preference use

trimmer(v1);
// or
v1.trimmer()

instead of

v1 = trim(v1);
// or
v1 = v1.trim();

Function Types

TYPE FUNCTION TYPE
var= traditional functions that return values and can be used in expressions and be on the right hand side of assignments
if traditional conditional statements that started with "if" or ended with "then/else" (or could have)
cmd traditional commands with no outputs
expr traditional commands that now have outputs and can be used in expressions

Parameters/Argument Types

in Parameters that provide data to the function. Can be variables or raw data like 1 or "X"
unspecified Same as "in". Omission of the most common type de-clutters the documentation. NB When defining your own subroutines and functions "in" cannot be omitted from the source code.
io Parameters that may provide and/or return data. Must be variables. Cannot be raw data like 1 or "X"
out Parameters that return data. Must be variables. Cannot be raw data like 1 or "X"

Optional Parameters

Key Default
= "" ""
= " " " "
= "." "."
= 1 1
= 0 0
= true true
= false false

Field mark characters

Exodus implements a PICK OS data structure called a "dynamic array". This is simply any string which uses six specific unprintable ASCII delimiter characters (\x1A to \x1F) to separate its various parts. The parts are referred to as records, fields, values, subvalues, text, and subtext and fall within each other.

Dynamic arrays therefore implement sparse six dimensional arrays.

Typical CPU caching architecture favours similar values being adjacent in memory therefore implementing them as strings of values separated by delimiter characters can have performance advantages over more complex structures.

In practice the vast majority of dynamic arrays consist of "fields" separated by the FM character (\x1E) but it is very common for fields to have values separated by the VM character (\x1D) and values to have subvalues using SM (\x1D).

Since the six delimiter characters fall in the unprintable character range certain other characters have been designated as usable for coding and printing. For example the FM character is represented as ^ and can be entered in source code appended with _var to indicate that the string must be converted to internal format.

var v1 = "f1^f2^f3"_var; // Three fields
var v2 = "f1^v1]v2]v3^f3"_var; // Three fields, 2nd field has 3 values. It is "multivalued".

Anything which contains a collection of fields can be considered as a "record" and records can be stored in files with a unique primary key. The fields might represent different columns of a traditional database table. So anonymous field number 1 might be a contact name, field 2 the contact address, field 3 a multivalued list of contact points. etc.

Delimiter
name
var Hex Display cstr " " char ' '
Record Mark RM \x1F ` _RM RM_
Field Mark FM \x1E ^ _FM FM_
Value Mark VM \x1D ] _VM VM_
Subvalue Mark SM \x1C } _SM SM_
Text Mark TM \x1B | _TM TM_
Subtext Mark STM \x1A ~ _STM STM_
Delimiter Name : Record Mark Field Mark Value Mark Subvalue Mark Text Mark Subtext Mark
var : RM FM VM SM TM STM
Display : ` ^ ] } | ~
Hex : \x1F \x1E \x1D \x1C \x1B \x1A
cstr " " : _RM _FM _VM _SM _TM _STM
char ' ' : RM_ FM_ VM_ SM_ TM_ STM_