/*, */, // , Prog, [, ] , Check , Bodied, Infix, Postfix, Prefix , IsBodied, IsInfix, IsPostfix, IsPrefix , OpPrecedence, OpLeftPrecedence, OpRightPrecedence , RightAssociative , LeftPrecedence, RightPrecedence , RuleBase , Rule , HoldArg , Retract , UnFence , HoldArgNr , RuleBaseArgList , MacroSet, MacroClear, MacroLocal, MacroRuleBase, MacroRule , Back-quoting , SetExtraInfo, GetExtraInfo , GarbageCollect , CurrentFile, CurrentLine , FindFunction , Secure .

Programming

This chapter describes functions useful for writing Yacas scripts.

/*, */, // comments
Prog, [, ] block of statements
Check report errors
Bodied, Infix, Postfix, Prefix define function syntax
IsBodied, IsInfix, IsPostfix, IsPrefix check for function syntax
OpPrecedence, OpLeftPrecedence, OpRightPrecedence get operator precedence
RightAssociative declare associativity
LeftPrecedence, RightPrecedence set operator precedence
RuleBase define function name
Rule define rule
HoldArg mark argument as not evaluated
Retract erase rules for a function
UnFence change local variable scope for a function
HoldArgNr specify argument as not evaluated
RuleBaseArgList obtain list of arguments
MacroSet, MacroClear, MacroLocal, MacroRuleBase, MacroRule define rules in functions
Back-quoting LISP back-quoting, macro expansion
SetExtraInfo, GetExtraInfo annotate objects with additional information
GarbageCollect do garbage collection on unused memory
CurrentFile, CurrentLine show current file and line of input
FindFunction find the file where a function is defined
Secure guard the host OS


/*, */, // -- comments

Internal function
Calling format:
/* comment */
// comment

Description:
Introduce a comment block in a source file, similar to C++ comments. // makes everything until the end of the line a comment, while /* and */ may delimit a multi-line comment.

Examples:
a+b; // get result
a + /* add them */ b;


Prog, [, ] -- block of statements

Internal function
Calling format:
Prog(statement1, statement2, ...)
[ statement1; statement2; ... ]

Parameters:
statement1, statement2 -- expressions

Description:
The Prog() and the [ ... ] constuct have the same effect: they evaluate all arguments in order and return the result of the last evaluated expression.

Prog(a,b); is the same as typing [a;b;]; and is very useful for writing out function bodies. The [...] construct is a syntactically nicer version of the Prog() call; it is converted into Prog(...) during the parsing stage.


Check -- report errors

Internal function
Calling format:
Check(predicate,"error text")

Parameters:
predicate -- expression returning True or False

Description:
If predicate doesn't evaluate to True, then current operation will be stopped, and execution will jump right back to the command line, showing error text. Use this to assure that some condition is met during evaluation of expressions (guarding against internal errors).


Bodied, Infix, Postfix, Prefix -- define function syntax

Internal function
Calling format:
Bodied("op", precedence)
Infix("op")
Infix("op", precedence)
Postfix("op")
Postfix("op", precedence)
Prefix("op")
Prefix("op", precedence)

Parameters:
"op" -- string, the name of a function

precedence -- nonnegative integer (evaluated)

Description:
Declares a function for the parser to understand as a bodied, infix, postfix, or prefix operator. Function name can be any string but meaningful usage would require it to be either made up entirely of letters or entirely of non-letter characters (such as "+", ":" etc.). Precedence can be specified (will be 0 by default).

Examples:
In> YY x := x+1;
CommandLine(1) : Error parsing expression

In> Prefix("YY", 2)
Out> True;
In> YY x := x+1;
Out> True;
In> YY YY 2*3
Out> 12;
In> Infix("##", 5)
Out> True;
In> a ## b ## c
Out> a##b##c;

See also:
IsBodied , OpPrecedence .


IsBodied, IsInfix, IsPostfix, IsPrefix -- check for function syntax

Internal function
Calling format:
IsBodied("op")
IsInfix("op")
IsPostfix("op")
IsPrefix("op")

Parameters:
"op" -- string, the name of a function

Description:
Check whether the function with given name "op" has been declared as a "bodied", infix, postfix, or prefix operator, and return True or False.

Examples:
In> IsInfix("+");
Out> True;
In> IsBodied("While");
Out> True;
In> IsBodied("Sin");
Out> False;
In> IsPostfix("!");
Out> True;

See also:
Bodied , OpPrecedence .


OpPrecedence, OpLeftPrecedence, OpRightPrecedence -- get operator precedence

Internal function
Calling format:
OpPrecedence("op")
OpLeftPrecedence("op")
OpRightPrecedence("op")

Parameters:
"op" -- string, the name of a function

Description:
Returns the precedence of the function named "op" which should have been declared as a bodied function or an infix, postfix, or prefix operator. Generates an error message if the string str does not represent a type of function that can have precedence.

For infix operators, right precedence can differ from left precedence. Bodied functions and prefix operators cannot have left precedence, while postfix operators cannot have right precedence; for these operators, there is only one value of precedence.

Examples:
In> OpPrecedence("+")
Out> 6;
In> OpLeftPrecedence("!")
Out> 0;


RightAssociative -- declare associativity

Internal function
Calling format:
RightAssociative("op")

Parameters:
"op" -- string, the name of a function

Description:
This makes the operator right-associative. For example:
RightAssociative("*")
would make multiplication right-associative. Take care not to abuse this function, because the reverse, making an infix operator left-associative, is not implemented. (All infix operators are by default left-associative until they are declared to be right-associative.)

See also:
OpPrecedence .


LeftPrecedence, RightPrecedence -- set operator precedence

Internal function
Calling format:
LeftPrecedence("op",precedence)
RightPrecedence("op",precedence)

Parameters:
"op" -- string, the name of a function

precedence -- nonnegative integer

Description:
"op" should be an infix operator. This function call tells the infix expression printer to bracket the left or right hand side of the expression if its precedence is larger than precedence.

This functionality was required in order to display expressions like a-(b-c) correctly. Thus, a+b+c is the same as a+(b+c), but a-(b-c) is not the same as a-b-c.

Note that the left and right precedence of an infix operator does not affect the way Yacas interprets expressions typed by the user. You cannot make Yacas parse a-b-c as a-(b-c) unless you declare the operator "-" to be right-associative.

See also:
OpPrecedence , OpLeftPrecedence , OpRightPrecedence , RightAssociative .


RuleBase -- define function name

Internal function
Calling format:
RuleBase("operator",{params})

Description:
Define a new rules table entry for a function "operator", with params as the parameter list.


Rule -- define rule

Calling format:
Rule("operator", arity,
  precedence, predicate) body

Description:
Define a rule for the function "operator" with "arity", "precedence", "predicate" and "body". The "precedence" goes from low to high: rules with low precedence will be applied first. The arity for a rules database equals the number of arguments. Different rules data bases can be built for functions with the same name but with a different number of arguments. Rules with a low value will be tried before rules with a high value, so a rule with precedence 0 will be tried before a rule with precedence 1.


HoldArg -- mark argument as not evaluated

Internal function
Calling format:
HoldArg("operator",parameter)

Parameters:
"operator" -- string, name of a function

parameter -- atom, symbolic name of parameter

Description:
Specify that parameter should not be evaluated before used. This will be declared for all arities of "operator", at the moment this function is called, so it is best called after all RuleBase calls for this operator.

The parameter must be an atom from the list of symbolic arguments used when calling RuleBase.

See also:
RuleBase , HoldArgNr , RuleBaseArgList .


Retract -- erase rules for a function

Internal function
Calling format:
Retract("function",arity)

Parameters:
"function" -- string, name of function

arity -- positive integer

Description:
Remove a rulebase for the function named "function" with the specific arity, if it exists at all. This will make Yacas forget all rules defined for a given function. Rules for functions with the same name but different arities are not affected.

Assignment := of a function does this to the function being (re)defined.

See also:
RuleBaseArgList , RuleBase , := .


UnFence -- change local variable scope for a function

Internal function
Calling format:
UnFence("operator",arity)

Parameters:
"operator" -- string, name of function

arity -- positive integers

Description:
When applied to a user function, the bodies defined for the rules for "operator" with given arity can see the local variables from the calling function. This is useful for defining macro-like procedures (looping and such).

The standard library functions For and ForEach use UnFence().


HoldArgNr -- specify argument as not evaluated

Standard library
Calling format:
HoldArgNr("function", arity, argNum)

Parameters:
"function" -- string, function name

arity, argNum -- positive integers

Description:
Declares the argument numbered argNum of the function named "function" with specified arity to be unevaluated ("held"). Useful if you don't know symbolic names of parameters, for instance, when the function was not declared using an explicit RuleBase call. Otherwise you could use HoldArg().

See also:
HoldArg , RuleBase .


RuleBaseArgList -- obtain list of arguments

Internal function
Calling format:
RuleBaseArgList("operator", arity)

Parameters:
"operator" -- string, name of function

arity -- integer

Description:
Returns a list of atoms, symbolic parameters specified in the RuleBase() call for the function named "operator" with the specific arity.

See also:
RuleBase , HoldArgNr , HoldArg .


MacroSet, MacroClear, MacroLocal, MacroRuleBase, MacroRule -- define rules in functions

Description:
These functions have the same effect as their non-macro counterparts, except that their arguments are evaluated before the required action is performed. This is useful in macro-like procedures or in functions that need to define new rules based on parameters.

Make sure that the arguments of Macro... commands evaluate to expressions that would normally be used in the non-macro versions!

See also:
Set , Clear , Local , RuleBase , Rule , Back-quoting .


Back-quoting -- LISP back-quoting, macro expansion

Internal function
Calling format:
`(expression)

Parameters:
expression -- expression containing "@var" to substitute the value of variable "var"

Description:
Back-quoting is a macro substitution mechanism. A backquoted expression is evaluated in two stages: first, variables prefixed by @ are evaluated inside an expression, and second, the new expression is evaluated.

To invoke this functionality, a back-quote ` needs to be placed in front of an expression. Parentheses around the expression are needed because the backquote binds tighter than other operators.

The expression should contain some variables (assigned atoms) with the special prefix operator @. Variables prefixed by @ will be evaluated even if they are inside function arguments that are normally not evaluated (e.g. functions declared with HoldArg()). If the @var pair is in place of a function name, e.g. "@f(x)", then at the first stage of evaluation the function name itself is replaced, not the return value of the function (see example); so at the second stage of evaluation, a new function may be called.

One way to view back-quoting is to view it as a parametric expression generator. @var pairs get substituted with the value of the variable var even in contexts where nothing would be evaluated. This effect can be also achieved using UnList() and Hold() but the resulting code is much more difficult to read and maintain.

This operation is relatively slow since a new expression is built before it is evaluated, but nonetheless back-quoting is a powerful mechanism that sometimes allows to greatly simplify code.

Examples:
This example defines a function that automatically evaluates to a number as soon as the argument is a number (a lot of functions do this only when inside a N(...) section).

In> Decl(f1,f2) := \
In>   `(@f1(x_IsNumber) <-- N(@f2(x)));
Out> True;
In> Decl(nSin,Sin)
Out> True;
In> Sin(1)
Out> Sin(1);
In> nSin(1)
Out> 0.8414709848;

This example assigns the expression func(value) to variable var. Normally the first argument of Set() would be unevaluated.

In> SetF(var,func,value) := \
In>     `(Set(@var,@func(@value)));
Out> True;
In> SetF(a,Sin,x)
Out> True;
In> a
Out> Sin(x);

See also:
MacroSet , MacroLocal , MacroRuleBase , Hold , HoldArg .


SetExtraInfo, GetExtraInfo -- annotate objects with additional information

Internal function
Calling format:
SetExtraInfo(expr,tag)
GetExtraInfo(expr)

Parameters:
expr -- any expression

tag -- tag information (any other expression)

Description:
Sometimes it is useful to be able to add extra tag information to "annotate" objects or to label them as having certain "properties". The functions SetExtraInfo and GetExtraInfo enable this.

The function SetExtraInfo returns the tagged expression, leaving the original expression alone. This means there is a common pitfall: be sure to assign the returned value to a variable, or the tagged expression is lost when the temporary object is destroyed.

The original expression is left unmodified, and the tagged expression returned, in order to keep the atomic objects small. To tag an object, a new type of object is created from the old object, with one added property (the tag). The tag can be any expression whatsoever.

The function GetExtraInfo(x) retrieves this tag expression from an object x. If an object has no tag, it looks the same as if it had a tag with value False.

No part of the Yacas core uses tags in a way that is visible to the outside world, so for specific purposes a programmer can devise a format to use for tag information. Association lists (hashes) are a natural fit for this, although it is not required and a tag can be any object (except the atom False because it is indistinguishable from having no tag information). Using association lists is highly advised since it is most likely to be the format used by other parts of the library, and one needs to avoid clashes with other library code. Typically, an object will either have no tag or a tag which is an associative list (perhaps empty). A script that uses tagged objects will check whether an object has a tag and if so, will add or modify certain entries of the association list, preserving any other tag information.

Note that FlatCopy() currently does not copy the tag information (see examples).

Examples:
In> a:=2*b
Out> 2*b;
In> a:=SetExtraInfo(a,{{"type","integer"}})
Out> 2*b;
In> a
Out> 2*b;
In> GetExtraInfo(a)
Out> {{"type","integer"}};
In> GetExtraInfo(a)["type"]
Out> "integer";
In> c:=a
Out> 2*b;
In> GetExtraInfo(c)
Out> {{"type","integer"}};
In> c
Out> 2*b;
In> d:=FlatCopy(a);
Out> 2*b;
In> GetExtraInfo(d)
Out> False;

See also:
Assoc , := .


GarbageCollect -- do garbage collection on unused memory

Internal function
Calling format:
GarbageCollect()

Description:
GarbageCollect() garbage-collects unused memory. The Yacas system uses a reference counting system for most objects, so this call is usually not necessary.

Reference counting refers to bookkeeping where in each object a counter is held, keeping track of the number of parts in the system using that object. When this count drops to zero, the object is automatically removed. Reference counting is not the fastest way of doing garbage collection, but it can be implemented in a very clean way with very little code.

Among the most important objects that are not reference counted are the strings. GarbageCollect() collects these and disposes of them when they are not used any more.

GarbageCollect() is useful when doing a lot of text processing, to clean up the text buffers. It is not highly needed, but it keeps memory use low.


CurrentFile, CurrentLine -- show current file and line of input

Internal function
Calling format:
CurrentFile()
CurrentLine()

Description:
The functions CurrentFile() and CurrentLine() return a string with the file name of the current file and the current line of input respectively.

These functions are most useful in batch file calculations, where there is a need to determine at which line an error occurred. It is easy to define a function

tst() := Echo({CurrentFile(),CurrentLine()});

which can then be spread out over the input file at various places, to see how far the interpreter reaches before an error occurs.

See also:
Echo .


FindFunction -- find the file where a function is defined

Internal function
Calling format:
FindFunction(function)

Parameters:
function -- string, the name of a function

Description:
This function is useful for quickly finding the file where a standard library function is defined. It is likely to only be useful for developers. The function FindFunction() scans the .def files that were loaded at start-up. This means that functions that were not defined in .def files, but were loaded directly, will not be found with FindFunction().

Examples:
In> FindFunction("Sum")
Out> "sums.rep/code.ys";
In> FindFunction("Integrate")
Out> "integrate.rep/code.ys";

See also:
Vi .


Secure -- guard the host OS

Internal function
Calling format:
Secure(body)

Description:
Secure evaluates body in a "safe" environment, where file opening and system calls are not allowed. This can protect the system when an unsafe evaluation is done, e.g. a script sent over the Internet to be evaluated on a remote computer.

See also:
SystemCall .