The XSM Macro language (SMAC)


Language Constructs

Data Types

The macro language provides a set of basic types that you may use in your programs:
int	An integer capable of storing signed whole numbers 
real	A real value
string	A sequence of ascii characters 
char	An ascii character
A variable can be declared using these types through a simple declaration either at the start of your program (visible to the entire program) or at the start of a function (only visible to that function). A typical declaration is of the form:
int sharenumber;
You can build on these basic types in two ways, arrays allow you to design storage areas that contain a sequence of data values of a particular type and structures allow you to combine multiple types within a larger container.

An array is declared through the use of the typedef statement. If you are used to the C language then, you should be aware that you must use typedef to produce arrays.

typedef real pricetype[100] 
pricetype prices;
In the example above a new type called pricetype is created. This new type consists of 100 reals. The program then defines the variable prices to be of the pricetype type. When you define an array in this manner, you can index each element within the array using [x], where x is the member of interest. All references start at 0.

A structure is produced with the struct statement. This allows any number of data types to be grouped together.

struct group {
	string securityname;
	real price;
};
This defines a structure named group that has fields called securityname (a string) and price (a real). Having defined a structure you may declare a variable newvar that is of this type with:
struct group newvar;
You can can also combine the definition of the structure with the declaration of the variable:
struct group {
        string securityname;
        real price;
} newvar;
It is also possible to use typedef with structures, for example to create a new type groupt that is an alias for the group structure you would use:
typedef struct group groupt

To reference fields within a structure simply use the . operator:

	newvar.securityname="security name not set";
	newvar.price=0.0;

You may store a date using the DATE type. The date type is set to a specific date with dialogdaterange and getdate commands. Most operators are defined on the date type and you can add a whole number of days to a date by simply adding an integer (the number of days) to the date in question.

Securities are stored using the SECURITY type. Typically this is set by calling the dialogsecurity function that prompts the user for a valid security name. Few operators are valid on the SECURITY type.

Statements

A statement in the macro language is the basic building block. A statement can consist of: All statements should be terminated with a semicolon. When multiple statements are combined a compound statement is produced. The macro language uses braces to specify a compound statement:
	{
	statement1;
	statement2;
	}

Expressions

An expression in the macro language can include an assignment. For example the expression:

	b=(a=10)+1
Will assign a to 10, b to 11 and has a value of 11.

A complete set of operators, with full precedence, is supported. The normal mathematical operations are available using the conventional symbols:

*	Multiplication
/	Division
+	Addition
-	Subtraction

For comparisons the operators are:

!=	Not equal
=	Equal
<	Less than
>	Greater than
<=	Less than or equal to
>=	Greater than or equal to

Other operators are:

^	Exclusive or
&	bitwise and
|	bitwise or
&&	logical and
||	logical or
(expr)	Parenthesis

You should note that not all types will support all operators.

Constants can be included within expressions. The available constants include integers, real numbers and strings enclosed within quotes.

Functions

A function allows you to group a number of statements together so that you may reuse the same code in multiple ways. A function is declared using the syntax:

typename function_name(arguments)
{
	variable_declarations

	statements
}
The typename declaration specifies the type that will be returned by the function and can be omitted if the function returns no values.

In the simplest form, the arguments used by a function are declared in the same way as normal variables but using a comma to delimit each argument. For example, if a function requires two arguments, the first being an integer and the second a string the declaration would be:

function_name(int num,string text)
{
	......
	......
}
This creates two variables for use by the function. The first num is the integer and the second, text, is the string. So if the function was called with:
function_name(10,"hello");
Then num would be 10 and text would be "hello". Note with this syntax the variables are strictly local so you CANNOT assign new values to them and have the caller see those new values. The assignment will only apply while the program is executing within the context of the function. Frequently you may wish to return multiple values from the function and therefore need to assign values that can be seen outside of the scope of the function. This can be achieved with the byref qualifier.
function_name(byref int num,byref string text)
{
	num=100;
}
When arguments are declared in this manner, the function is able to modify the actual values passed by the caller:
a=200;
function_name(a,"hello");
print(a);
In this instance the value that will be displayed will be 100 and NOT 200. If the byref keyword had not been included, the value 200 would have been shown.

When you wish to pass structures to a function, either by value or by reference, you will need to declare the argument that is a structure using a real type and not with the struct keyword. For example:

struct simplestruct {
	.....
	};

typedef simplestruct structtype;

function(byref structtype arg)
{
	....	
	.....
}
This same method should also be employed when you wish to pass arrays to functions.

Functions can return values using the return command. The type that is to be returned should be specified in front of the function name. For example if the macro has a function called createname that returns a string the function would be written:

string createname()
{
	....
	....
}

At any point within the function, a call can be made to return command passing a value that evaluates to a string. If you need to be able to return, prior to the last statement in a function, from a function that has no return value then you can simply use return without an argument.

Conditional Expressions

The macro language provides a complete if..then..else construct:

	if (expression)
		statement
	else
		statement
The else part of the construct is entirely optional and can be omitted when not necessary. At execution time, the expression within the if statement is evaluated. When it is non-zero (TRUE), the first statement will be executed. If it is zero and an else clause is specified, the second statement following the else will be executed. Although the expression in the if statement is treated as having boolean properties, it can be of any integer type.

The while and do..while Constructs

The while construct provides the basic building block for iteration within macros. The while construct allows a statement (including a compound statement) to be continually executed:

while (condition) {
	.....
	code
	.....
	.....
}
The statements within the braces will be executed until the boolean expression given by condition becomes FALSE. With most macros it is usually that the code that is executed within the loop will eventually result in the condition becoming FALSE. If the condition is FALSE before the loop starts then the loop will not be executed. If the loop should always be executed at least once, then the do..while construct should be used:
do {
	.....
	code
	.....
	.....
} while (condition);
The loop is executed once and the condition is evaluated. If it is FALSE the loop will be executed from the top, otherwise the statement following the while() command will be executed.

Within a while (or do...while) loop, the continue and break commands may be used. The continue statement takes the point of execution back to the top of the loop. The break command will change the point of execution to the first statement that follows the loop.

The for construct

The for construct is based upon the while construct. The statement

for( i=0; i<100; i=i+1)
	print(i,"\n");
Is equivalent to the statement:
i=0; 
while (i<100) {
	print(i,"\n");
	i=i+1;
}
Unlike C or C++ you must specify all three expressions within the for statement.

Builtin Constants


PI

The constant PI.


NOPRICE

The NOPRICE constant gives a real number that indicates that no valid price was available. These are used by the price database and by the graph plotting functions to indicate no information and in the case of graph plotting, that interpolation/extrapolation is required.

Builtin Mathematic Functions

acos(x)

Returns the arc cosine of x in the range of 0 to PI. x should be in the range of -1 to 1.


asin(x)

Returns the arc sine of x in the range of -PI/2 to PI/2 radians. x should be in range of -1 to 1.


atan(x)

Returns the arc tangent of x in the range -PI/2 to PI/2 radians.


cos(x)

The cos function returns the cosine of its argument x measured in radians.


sin(x)

The sin function returns the sine of its argument x measured in radians.


tan(x)

Returns the tangent of x measured in radians.


cosh(x)

Returns the hyperbolic cosine of x.


sinh(x)

Returns the hyperbolic sine of x.


tanh(x)

Returns the hyperbolic tangent of x.


exp(x)

Returns e^x.


log(x)

Returns the natural logarithm of x. Argument x must be positive.


log10(x)

Returns the logarithm base ten of x. Argument x must be positive.


sqrt(x)

Returns the square root of x. Argument x must be positive.


ceil(x)

Returns the smallest integer not less than x.


fabs(x)

Returns the absolute value of x.


floor(x)

Returns the largest integer not greater than x.


pow(x,y)

Returns x^y.


fmod(x,y)

Returns the real remainder of the division of x by y.


atan2(x,y)

Returns the arc tangent of y/x in the range of -PI to PI.


Using Windows

windowopen(name,refreshfnc,menu)
string name;
string refreshfnc;
MENU menu;

Opens a window with the title specified by name. The function will return a value of type WINDOW and this value should be used in future window based functions when this window is to be referenced. The argument refreshfnc names the function that will be executed when the window is resized and menu specifies the menus that should be attached to the window.


windowclose(window) WINDOW window;
Closes the window specified by window.

clearpage(window) WINDOW window;
Clears the window specified by window.


drawpage(window) WINDOW window;
Ensures the window specified by window is up to date.

drawto(window,x,y) WINDOW window; real x,y;
Draws a line from the current position in window to the coordinate x,y.

moveto(window,x,y) WINDOW window; real x,y;
Moves the current position in window to the coordinate x,y.

size(window,width,height) WINDOW window; real width,height;
Sets width and height to the current width and height of the window window.

axis_date(window,date1,date2,min_y,max_y) WINDOW window; DATE date1,date2; real min_y,max_y;
Draws a complete axis in the window window. On the X axis dates will range from date1 to date2 and on the Y axis values will go from min_y to max_y.

plot(window,data,maxsize, start, end) WINDOW window; typedef real realarray[x] realarray data; int maxsize; int start,end;
Plots the data given by data in window using information previously set by axis_date. The maxsize argument should specify how many numbers are stored in data. The start and end arguments give the range of indices within data that should be plotted.

Note that the information in data may be sparse. If a value is not known and extrapolation is required then the value in question should be set to the constant NOPRICE


linewidth(window,width) WINDOW window; real width;
Sets the line width for window.

colour(window,colour)

color(window,colour) WINDOW window; string colour,color;

Sets the current colour for window.

Using Menus

menuopen(menu)
MENU menu;
Opens the specified menu. The menu can then be built and passed to a windowopen() call:

	WINDOW win;
	MENU mymenu;

	....
	....

        menuopen(mymenu);
        menu(mymenu,"Project","Exit","window_exit");
        menu(mymenu,"Project","Select security","selectsecurity");
        win=windowopen("Prices of a security","window_refresh",mymenu);


menuclose(menu) MENU menu;
Closes the specified menu.

menu(menu,barname,itemname,functionname) MENU menu; string barname; string itemname; string functionname
Adds the menu bar and item to the specified menu that should already have been opened with menuopen. The menu item itemname should appear in the bar barname. The function named functionname will be called when the menu item is selected by the user.

Using Files

fileopen(name,access)
string name;
string access;

Opens the file specified by name with the access permissions specified by the access string. The access permissions are convential stdio values:

         r    reading

         w    create for writing (or truncate if file already exists)

         a    append at end of file

         r+   open for reading and writing

         w+   truncate or create for update

         a+   open (append) or create for update

The returned value from fileopen is of type FILE and should be assigned to a variable of FILE type. The file can be read or written as appropriate. However before the file is accessed it is recommended you use eof() to determine that the file is correctly open. A typical code fragment for reading from a file would be:


	FILE file;
	string input;

	....
	....
	
        file=fileopen("pricedump","r");
        if ( eof(file)== 2) {
                print("Could not open file");
                return;
        }
	.....
        while (eof(file)==0) {
                filereadline(file,input);
                print(input,"\n");
        }
        fileclose(file);


fileclose(file) FILE file;

Closes a previously opened file. A file for which eof() returns 2 does not need to be closed with this function.


fileprint(file,arg1,...,argn) FILE file; (any) argi;

Writes the arguments arg1 to argn to the specified file in ascii format. The arguments can be of any type, provided they have an ascii representation. Those that types that are appropriate include:


filewrite(file,arg1,...,argn) FILE file; (any) argi;
Writes the arguments arg1 to argn to the specified file in binary format. Although all types can be written using this function, the output file may not be transferable or readily readable.


fileread(file,arg1,..,argn) FILE file; (any) argi;
Reads the arguments arg1 to argn from the file. The arguments can be of any type. Typically to read ascii files you will need to use the filereadline() command that is able to interpret files on a line by line basis.


filereadline(file,arg1,..,argn) FILE file; string argi;
Reads the arguments arg1 to argn from the file. The arguments must be of type string. Each line in the file will be read into the relevant argument.

eof(file) FILE file;

Returns the state of the file:

This function allows one to read from a file while there is data to read:

        file=fileopen("pricedump","r");
	if ( eof(file)== 2) {
		print("Could not open file");
		return;
	}
        while (eof(file)==0) {
                filereadline(file,input);
                print(input,"\n");
        }
        fileclose(file);
 

Other Functions


mainmenu(barname,itemname,funcname) string barname; string itemname; string funcname;

Attaches a new menu item to a bar on the main window. For example to create a new menu option for the 'Project' menu one would use:

        mainmenu("Project","Write prices to file","dump_prices");
This will result in the function dump_prices being called whenever the 'Write prices to file' menu item is selected.



dialogsecurity(title) string title;

Allows the user to select a security from all the securities that are known to the system. This returns a value of type SECURITY.

	SECURITY security;
	
	....
	....

        security=dialogsecurity("Enter a security");


int dialogyesno(message,okmsg,cancelmsg) string message; string okmsg; string cancelmsg;

Displays a yes/no dialog with the message specified. The "OK" button will be labeled with the string specified by okmsg and the "CANCEL" button will be labeled by cancelmsg. The function returns 1 if the user selects "OK", otherwise 0 will be returned.


dialogmessage(message) string message;

Displays a dialog with the message specified.


int dialogstring(windowtitle, message, stringedit,length) string windowtitle; string message; string stringedit; int length;

Displays a dialog where the user can edit the string given by stringedit. The maximum length of the string is given by length. The dialog will be in a window with the title given by windowtitle and the string edit area will be labeled with message.



int dialogdaterange(title,startdate,enddate) string title; DATE startdate; DATE enddate;

Allows the user to select a range of dates starting with the date specified by startdate and finishing with enddate. If the user hits the "OK" button then the function returns 1, else the function returns 0 indicating the user canceled the operation. When 1 is returned the startdate and enddate arguments will be set to the new date range.



getdate(date,stringdate)

getdate(date) DATE date; string stringdate;

Converts a date in ascii form to a date (in the first form) or sets a date to today (in the second form).


int date2days(date) DATE date;
Converts a date to the number of days elapsed since 1st Jan 1970.


getprices(security,data,date,num)

SECURITY security; typedef real realarray[x]; realarray data; DATE date; int num;

Fetches the prices for the specified security. The array given by data should be large enough to store all the prices for the given security. The date that data[0] represents will be returned in date and the total number of prices in the array will be returned in num.


int isaprice(price) real price;
Returns TRUE if the value given by price represents a valid price. Not all real numbers are prices so as to allow information to be held in the price database indicating that no price exists for a given date. See the constant
NOPRICE

int system(cmdstring) string cmdstring;
Executes a shell with the commands given in cmdstring. The return value will be 0 if the command executed correctly, otherwise it will be -1 if a process could not be forked or -127 if the commands could not otherwise be executed.