All scalar types can have units associated. Available units are:
mm | Millimeters |
in | Inch (25.4mm) |
mil | Mil = 1/1000 inch (0.0254mm); always converted to inch |
deg | Degrees, 360° full circle |
rad | Radians, 2π full circle |
| No units (none) |
Examples:
- 10.34mm
- 200.7mil (becomes 0.2007in)
- 1.125in
- 5432
- 60deg
- 1.57079632679rad
Note that mils ("mil") are always converted to inch as soon as the value is
parsed.
Calculations with units is implicit in the grammar. The resulting units are
derived from the left-hand side of the expression, with the following rules
(<none> indicates no unit):
<none> | +-*/% | <none> | → <none> |
<none> | +-*/% | mm | → mm |
<none> | +-*/% | in | → in |
<none> | +-*/% | deg | → deg |
<none> | +-*/% | rad | → rad |
mm | +-*/% | <none> | → mm |
mm | +-*% | mm | → mm |
mm | / | mm | → <none> |
mm | +-*% | in | → mm |
mm | / | in | → <none> |
mm | +-*/% | deg | → Warning [mm] |
mm | +-*/% | rad | → Warning [mm] |
in | +-*/% | <none> | → mm |
in | +-*% | mm | → in |
in | / | mm | → <none> |
in | +-*% | in | → in |
in | / | in | → <none> |
in | +-*/% | deg | → Warning [in] |
in | +-*/% | rad | → Warning [in] |
deg | +-*/% | <none> | → deg |
deg | +-*/% | mm | → Warning [deg] |
deg | +-*/% | in | → Warning [deg] |
deg | +-*% | deg | → deg |
deg | / | deg | → <none> |
deg | +-*% | rad | → deg |
deg | / | rad | → <none> |
rad | +-*/% | <none> | → rad |
rad | +-*/% | mm | → Warning [rad] |
rad | +-*/% | in | → Warning [rad] |
rad | +-*% | deg | → rad |
rad | / | deg | → <none> |
rad | +-*% | rad | → rad |
rad | / | rad | → <none> |
Only mm/in, in/mm, deg/rad and rad/deg conversions will affect the actual
magnitude of the values on which the operation is performed. Examples:
10mm + 200mm | → 210mm | |
10mm + 200in | → 5180.0mm | (converted to floating point) |
10mm + 200mil | → 15.08mm | (implicit floating point due to mil) |
10mm + 200 | → 210mm | |
10 + 200 | → 210 | |
10in + 200 | → 210in | |
10in + 200mm | → 17.874015748in | (converted to floating point) |
10in + 200in | → 210in | |
10in + 200mil | → 10.2in | (implicit floating point due to mil) |
| | |
1deg + 1deg | → 2deg | |
1deg + 1rad | → 58.29577951deg | (converted to floating point) |
1deg + 1 | → 2deg | |
1 + 1 | → 2 | |
1rad + 1deg | → 1.01745329rad | (converted to floating point) |
1rad + 1rad | → 2rad | |
1rad + 1 | → 2rad | |
Distance unit conversion at output: When G-code is output, then all
units are converted to millimeters or inches for axes XYZ and UVW depending the
-i option (without -i:G21/mm; with -i:G20/in). Values on axes XYZ and UVW with
no units associated are treated as if they were millimeters or inches as
appropriate for the respective axis depending the -i option.
Angular unit conversion at output: Axes ABC will always output values in
degrees, as per G-code standard. However, the values for the ABC axes without
units associated are interpreted as
radians and are converted to
degrees. The behaviour has been changed in version 1.9.0, where all angular
arguments now default to radians for consistency if no units were associated.
You must add the --degrees option to gcmc if you want the old behaviour. The
--degrees option only has influence on the angular units for move() and goto()
vectors on the ABC axes if no units were associated to the values.
Important note: Portable programs between imperial and metric mode
must use units consistently. Omitting units on some values may cause
calculations to be thrown off by a large factor due to implicit conversions of
values with no units associated. Also conversions between degrees and radians
may cause surprises, if units are not explicitly associated.
It is always
good practice to use units consistently throughout the entire program.
Calculations are performed with unary and binary operators with following precedence:
Rank | Operator | Evaluation | Description |
1 | left-to-right | parenthesis |
2 | left-to-right | vector/vector-list index, vector field, post-increment, post-decrement |
3 | right-to-left | unary: logical not, binary not, plus, minus, pre-increment, pre-decrement |
4 | left-to-right | multiplication, division, modulo |
5 | left-to-right | binary plus, minus, plusor and minusor |
6 | left-to-right | shift left, shift right |
7 | left-to-right | comparison |
8 | left-to-right | comparison |
9 | left-to-right | binary and |
10 | left-to-right | binary xor |
11 | left-to-right | binary or |
12 | left-to-right | logical and |
13 | left-to-right | logical or |
14 | right-to-left | ternary/conditional expression |
15 | right-to-left | assignment |
Valid operations and result:
scalar | + | scalar | → | scalar |
vector | + | vector | → | vector |
vectorlist | + | vector | → | vectorlist |
vectorlist | + | vectorlist | → | vectorlist (append) |
string | + | scalar | → | string (printf scalar) |
string | + | vector | → | string (printf vector) |
string | + | vectorlist | → | string (printf vectorlist) |
string | + | string | → | string (concat) |
scalar | - | scalar | → | scalar |
vector | - | vector | → | vector |
vectorlist | - | vector | → | vectorlist |
scalar | * | scalar | → | scalar |
scalar | * | vector | → | vector |
scalar | * | vectorlist | → | vectorlist |
vector | * | scalar | → | vector |
vector | * | vector | → | scalar (dot product) |
vectorlist | * | scalar | → | vectorlist |
scalar | / | scalar | → | scalar |
vector | / | scalar | → | vector |
vectorlist | / | scalar | → | vectorlist |
scalar | % | scalar | → | scalar |
vector | % | scalar | → | vector |
vectorlist | % | scalar | → | vectorlist |
scalar | << | scalar | → | scalar |
vector | << | scalar | → | vector |
vectorlist | << | scalar | → | vectorlist |
scalar | >> | scalar | → | scalar |
vector | >> | scalar | → | vector |
vectorlist | >> | scalar | → | vectorlist |
scalar | | | scalar | → | scalar |
vector | | | vector | → | vector (merge values on left-hand side) |
scalar | & | scalar | → | scalar |
vector | & | vector | → | vector (replace values on left-hand side) |
scalar | ^ | scalar | → | scalar |
Operators +| and -| work the same as + and - with the exception of how undef
values are handled. See below for details.
Integer vs. floating point
Gcmc makes a distinction between integer and floating point values.
Calculations performed on integers give integer result. Calculations on
floating point or mixed integer/floating point gives floating point result.
This behavior is regardless associated units unless unit-conversion is
implied.
As a consequence, all operations with integers, including divisions, result in
integers and are therefore implicitly rounded calculations. This can give rise
to problems in a program if exact results are required. You should always use
floating point values is you require exact results.
1 / 10 → 0
1.0 / 10 → 0.1
1 / 10.0 → 0.1
1.0 / 10.0 → 0.1
1mm / 10 → 0mm
1.0mm / 10 → 0.1mm
1mm + 1in → 26.4mm
EPSILON calculation
All floating point numbers are considered equal if they are within |1e-12| of
each other. The value 1e-12 is called EPSILON and is used in all comparisons as
well as (internal) conversions from floating point to integer. Limiting the
precision of floating point has the advantage of allowing rounding errors to be
disregarded more easily. This is especially important when calculating arcs,
where otherwise small errors may cause the arc radius versus endpoint
calculation to fail.
a = 0;
(a + 1.0e-12) == a
(a + 0.9e-12) == a
The value 1e-12 allows for femto-meter accuracy and is four..five orders of
magnitude better than the covalent bond-radius of atoms, which, for all
practical purposes and intents, should be enough for a mill.
All implicit conversions from floating point to integer also use EPSILON
calculation with the following rule:
n-EPSILON < n < n+EPSILON. A warning is
emitted if the value of
n is outside the EPSILON range whenever implicit
conversions takes place. Rounding on explicit conversion (using the to_int()
function) will not generate a warning, but a value within EPSILON range of the
nearest integer value will be converted to that nearest integer value.
a = 1.0;
to_int(1.0 + 0.9e-12);
to_int(1.0 - 0.9e-12);
to_int(1.0 + 1.0e-12);
to_int(1.0 - 1.0e-12);
Undef handling
Undefined values may be used in some calculations, resulting in a real value or
undefined. Rules for calculating with undefined are:
undef | + | undef | → | undef |
undef | + | x | → | undef |
x | + | undef | → | x |
undef | +| | undef | → | undef |
undef | +| | x | → | x (if x is scalar, else error) |
x | +| | undef | → | x |
undef | - | undef | → | undef |
undef | - | x | → | undef |
x | - | undef | → | x |
undef | -| | undef | → | undef |
undef | -| | x | → | -x (as 0-x) |
x | -| | undef | → | x |
undef | * | undef | → | undef |
undef | * | x | → | undef (if x is scalar, else error) |
x | * | undef | → | undef |
undef | / | undef | → | undef |
undef | / | x | → | undef (if x is scalar, else error) |
x | / | undef | → | undef |
undef | % | undef | → | undef |
undef | % | x | → | undef (if x is scalar, else error) |
x | % | undef | → | undef |
undef | << | undef | → | undef |
undef | << | x | → | undef |
x | << | undef | → | x |
undef | >> | undef | → | undef |
undef | >> | x | → | undef |
x | >> | undef | → | x |
The rules also apply to vector operations +, -, +| and -| where both values are
vectors, or scalar multiplication/division of a vector/vector-list. Such cases
will operate on the vector's coordinates using above rules.
You can assign a literal undef to a variable by using the following notations:
undefvar = [-][0];
undefvar = undef();
The literal undef is actually a vector with one undefined coordinate from which
you request the first entry, which happens to be undef.
Local variables in functions are undef when declared without an assignment.
Please note that the undef interpretation for +| and -| has consequences when
translating vectors and vectorlists by a fixed offset. The calculated
sum/difference is not undef preserving, which may cause unintended side-effects
when subsequent function calls interpret undef values with special care. For
example, a move() or goto() with an undef coordinate omits the coordinate in
its output entirely. Adding an offset to the vector may convert an undef to a
value, which is then translated into movement, which may not be appropriate.
Add and subtract operators
Addition and subtraction on scalars, vectors and vectorlists are handled as
expected when operating on integer or floating point values. Care must be taken
when handling undef values.
Gcmc version 1.5.0 has changed the + and - operator's behavior to be an
exclusive add and subtract and introduced the operators +| and -| to be
inclusive add and subtract. Inclusive add/subtract treats the left-hand
side as zero if it is undef, whereas exclusive add/subtract treats operations
on left-hand side undef values as undef result. The difference between the
operator's functionality can be illustrated as follows:
Exclusive Add (+), left-hand side undef is retained, right-hand side undef substituded by 0 |
[15, -, -2] + [-, 10] | | is: | → | | or: |
| | [15, -, -2] | | | [15, -, -2] |
| + | [ -, 10] | | + | [ 0, 10, 0] |
| |
| → | |
|
| | [15, -, -2] | | | [15, -, -2] |
|
Inclusive Add (+|), both left- and right-hand side undef substituted by 0 |
[15, -, -2] +| [-, 10] | | is: | → | | or: |
| | [15, -, -2] | | | [15, 0, -2] |
| +| | [ -, 10] | | +| | [ 0, 10, 0] |
| |
| → | |
|
| | [15, 10, -2] | | | [15, 10, -2] |
|
Exclusive Subtract (-), left-hand side undef is retained, right-hand side undef substituded by 0 |
[15, -, -2] - [-, 10] | | is: | → | | or: |
| | [15, -, -2] | | | [15, -, -2] |
| - | [ -, 10] | | - | [ 0, 10, 0] |
| |
| → | |
|
| | [15, -, -2] | | | [15, -, -2] |
|
Inclusive Subtract (-|), both left- and right-hand side undef substituted by 0 |
[15, -, -2] -| [-, 10] | | is: | → | | or: |
| | [15, -, -2] | | | [15, 0, -2] |
| -| | [ -, 10] | | -| | [ 0, 10, 0] |
| |
| → | |
|
| | [15, -10, -2] | | | [15, -10, -2] |
You should be aware that reversing the left-hand side and the right-hand side
gives a different result:
• The exclusive add operation is
not commutative (a+b is not equal b+a) and is undef preserving.
• The inclusive add operation is
commutative (a+b equals b+a) and is undef replacing.
The difference is illustrated by following example and you should compare it
to the example above:
Exclusive Add (+), left-hand side undef is retained, right-hand side undef substituded by 0 |
[-, 10] + [15, -, -2] | | is: | → | | or: |
| | [ -, 10] | | | [ -, 10, -] |
| + | [15, -, -2] | | + | [15, 0, -2] |
| |
| → | |
|
| | [ -, 10, -] | | | [ -, 10, -] |
|
Inclusive Add (+|), both left- and right-hand side undef substituted by 0 |
[-, 10] +| [15, -, -2] | | is: | → | | or: |
| | [ -, 10] | | | [ 0, 10, 0] |
| +| | [15, -, -2] | | +| | [15, 0, -2] |
| |
| → | |
|
| | [15, 10, -2] | | | [15, 10, -2] |
Boolean logic and comparison operators
Boolean operations result in an integer value with no units of either 1 (true)
or 0 (false). Boolean AND (&&) and OR (||) are evaluated using
short-circuit evaluation. I.e. the right-hand side is not evaluated if the
left-hand side of the expression pre-determines the outcome.
- Any scalar value not zero (0) within |EPSILON| is considered to be true
- Undef values are considered to be false
- Vector are true if they contain at least one coordinate, regardless what the coordinate contains
- Vectorlists are true if they contain at least one vector, regardless what the vector contains
- String values are true if not empty and false if empty
Comparison operators operate on scalars, vectors and strings, with the
limitation that vector comparison only supports == and !=. Scalar comparison
tests the units of the scalars and emits a warning if a mismatch is detected.
Strings are compared using case-sensitive comparison at Unicode character-level
using the wcscmp() C-function.
Comparing vectors requires them to have the same number of entries. Comparing
vectors with unequal number of entries results in a warning and the comparison
result is always false. Each vector entry obeys the same unit rules as for
scalars and warnings are emitted on mismatches. An undef vector entry is only
equal to another undef entry.
Binary Boolean operators
Binary operators '&', '|', '^' and '~' on scalars only work on integers
with no associated units. Floating point values are converted to integer and
units are stripped when encountered. A warning is emitted if floating point
values are used or units are encountered. An integer has at least 32 bit
resolution, but may also be 64 bit wide. Therefore, the binary not (~) operator
may differ depending platform. However, you can use one's (or two's) complement
math to know the actual value.
val = (1<<2) | (1<<4);
val = 0x5a & 0x0f;
val = ~1;
Binary operators on vectors merge ('|') and replace ('&') values from the
right side vector into the left side vector. Merging values only occurs on
entries that are undef on the left side and not undef on the right side.
Replacing values occurs for all values where a non-undef value exists in both
left and right side.
[-, 2, 3] | [4, 5] → [4, 2, 3]
[1, -, 3] | [4, 5] → [1, 5, 3]
[1, 2, -] | [4, 5] → [1, 2, -]
[-, 2, 3] & [4, 5] → [-, 5, 3]
[1, -, 3] & [4, 5] → [4, -, 3]
[1, 2, -] & [4, 5] → [4, 5, -]
Index [] operator
Index operator [] works on both lvalue and rvalue. Index values must be scalar
and should have no units associated. Negative indices address the vector or
vectorlist from the end. Stronger restrictions apply to lvalues than rvalues,
where lvalues must address a variable. Both single and double indexing is
supported. Double indexes can only be performed on vectorlists. Lvalue indices
may address locations that are not yet assigned, whereas rvalue indices result
in a warning if the index is out of bounds. Examples:
vector = [1, 2, 3];
vector[2] = 6;
vector[3] = vector[-1];
vector[7] = 2;
vlist = {};
vlist[2] = [1, 2];
vlist[1][3] = 3.1415;
myvec = {[1,2], [2,3]}[1];
myval = [1, 2, 3][1];
Field . operator
All vectors may have the first nine entries addressed as fields for more
natural readability. The field-names correspond with the axis-name for the
entry in lower case letters (x, y, z, a, b, c, u, v, w).
Field addressing a vector is translated into an
index operation,
as explained above and follow the rules of indexing. Examples:
vector = [1, 2, 3];
call_xy(vector.x, vector.y);
vector.z = 6;
vector.a = vector[-1];
vector.v = 2;
Shift operators
Shift operators << and >> work as usual on scalars in which
<< multiplies by 2 and >> divides by 2, without modifying
associated units. Left shift on vectors and vectorlists will delete values from
the start. Right shift on vectors adds undef values to the start and on
vectorlists it will add empty vectors at the start. Shift operators always
return the left-hand side type, including its units. The right-hand side should
not have any units associated. Examples:
1 << 2 | → | 4 |
6 >> 1 | → | 3 |
[1, 2] << 1 | → | [2] |
[1, 2] >> 2 | → | [-, -, 1, 2] |
{[1,2], [3,4]} << 1 | → | {[3,4]} |
{[1,2], [3,4]} >> 1 | → | {[], [1,2], [3,4]} |
Ternary operator
Ternary operator ?: for conditional expressions does not perform any checks on
the return-type of the true- and false-clauses. This means that the expression
may evaluate to different types depending the condition. This may or may not be
useful, so you should beware when using ternary operators.
Dot product
The dot product of two vectors will have units associated if either vector has
any entry with distance units. The resulting units are set to millimeters or
inches depending gcmc's operating mode (-i option). The dot product
multiplication/sum sequence will perform conversion to the appropriate distance
unit on the vector entries before multiplication is performed.
Beware: the
magnitude of the dot product
depends on the units
selected. Calculating a dot product with angular units will cause a
warning to be emitted. If all vector entries are unit-less, then the result
will also remain unit-less.
Running in metric mode:
vnn = [2.0, 2.0];
vmm = [1.0mm, 2.0mm];
vin = [2.0in, 1.0in];
dotp = vnn * vnn;
dotp = vnn * vmm;
dotp = vnn * vin;
dotp = vmm * vmm;
dotp = vin * vin;
dotp = vmm * vin;
dotp = vin * vmm;
Running in imperial mode (-i command-line option):
vnn = [2.0, 2.0];
vmm = [1.0mm, 2.0mm];
vin = [2.0in, 1.0in];
dotp = vnn * vnn;
dotp = vnn * vmm;
dotp = vnn * vin;
dotp = vmm * vmm;
dotp = vin * vin;
dotp = vmm * vin;
dotp = vin * vmm;
Portable use of the dot product may pose a challenge if used carelessly and can
result in unforeseen problems. Most uses of the dot product involve extracting
the cos(φ) part (the angle between the vectors), in which case you will not
have too many problems:
vmm = [1.0mm, 2.0mm];
vin = [2.0in, 1.0in];
cosphi = (vmm * vin) / (length(vmm)*length(vin));
Note: The division by the length of both vectors is in parenthesis () to
ensure that the result has no units. Two separate divisions would wrongly
propagate the units from the second division to the result.
If vector coordinates with and without units are combined, then there will be a
difference due to default conversions and a wrong result may be calculated:
vmm = [1.0, 2.0mm];
vin = [2.0in, 1.0in];
cosphi = (vmm * vin) / (length(vmm)*length(vin));
You can retrieve the cos(φ) value more easily by using normalized vectors.
Normalizing the vectors eliminates the division. However, you should still
ensure that the source vectors have units on all coordinates:
vmm = normalize([1.0mm, 2.0mm]);
vin = normalize([2.0in, 1.0in]);
cosphi = vmm * vin;
String operations
Strings can be added using the + operator to concatenate the strings.
Conversion to string is performed if the right-hand side of the + operator is
scalar, vector or vectorlist. Comparing strings uses a binary Unicode
character-level compare, is case-sensitive and unaware of Unicode's internals
or specific character sets.
i = 1;
v = [1, 10mm, 2.0in];
str = "Hello" + " " + "World!";
str = "val=" + i;
str = "val=" + v;
A variable is any word starting with a letter or _ and followed by letters,
numbers or _ that has not been reserved as a keyword. Variables can be assigned
values in statements. Each statement is terminated with a semi-colon (;).
Reserved words are:
• |
break |
break any loop construct |
• |
const |
declare variable as constants |
• |
continue |
continue to start of loop |
• |
return |
return from function |
• |
for |
for loop construct |
• |
foreach |
foreach loop construct |
• |
while |
while loop construct |
• |
do |
do/while loop construct |
• |
repeat |
repeat loop construct |
• |
if |
conditional |
• |
elif |
else-if conditional |
• |
else |
final conditional clause |
• |
function |
function definition |
• |
local |
local variable scope declarator |
• |
include |
include other file |
• |
in |
inch measurement modifier |
• |
mil |
mil measurement modifier (0.001") |
• |
mm |
millimeter measurement modifier |
• |
deg |
degree measurement modifier |
• |
rad |
radians measurement modifier |
All statements consist of an expression. An expression can be a constant, a
variable or any combination with an operator. Assignments are also expressions,
which allows cascade-able assignments, and are evaluated strictly
right-to-left. Assignments have restrictions on the lvalue expression they can
address while they accept any rvalue expression. Lvalues are variables and
indexed variables. Rvalues may be any expression. Examples:
var123 = [1, 2, 3, 4, 5];
feedrate(125.77mm);
_xx = 4.5mm;
yy = 50mil;
vec = [_xx, yy];
list = { vec, [_xx, yy], [1, 2, 3] };
a = vec[0];
b = list[2];
val1 = val2 = val3 = 0;
Constants
Variables may be declared as constant using the
const
keyword. The declaration must include an assignment from an expression which
results in a value. Any subsequent assignment to constants is prohibited and
results in a run-time error. Constants may be
passed as reference
in function calls, but any assignment to the local reference will then be
flagged as an error. Variables declared
const
are local to the scope in which they are declared, just like variables declared
using
local in functions.
const constinteger = 1234;
const constfloat = 5.678;
const constvector = [1, -, 2];
const constlist = {[1], [2]}
constinteger = 7890;
const FLAG_UP = 0x01, FLAG_DOWN = 0x02;
const FLAG_LEFT = 0x04, FLAG_RIGHT = 0x08;
Predefined constants
Gcmc defines a set of constants before any command-line defines are parsed and
before the script is executed. The constants are useful for making calls to
functions more readable. The
function reference
states the names of the constants in the description of the arguments.
The following table shows all other constants currently defined by gcmc:
• |
GCMC_VERSION_STR |
string |
Complete version number with dots |
• |
GCMC_VERSION |
integer |
Complete version number calculated as "(major * 1000 + minor) * 1000 + point" |
• |
GCMC_VERSION_MAJOR |
integer |
Version number major part |
• |
GCMC_VERSION_MINOR |
integer |
Version number minor part |
• |
GCMC_VERSION_POINT |
integer |
Version number point release |
Program flow is controlled by standard conditional and loop control statements:
- if(cond) { ... }
- if(cond) { ... } elif(cond) { ... } [elif*]
- if(cond) { ... } else { ... }
- if(cond) { ... } elif(cond) {} [elif*] else { ... }
- foreach(list; ident) { ... }
- for(stmt; cond; stmt) { ... }
- while(cond) { ... }
- do { ... } while(cond);
- repeat(scalar) { ... }
- repeat(scalar; ident) { ... }
- return expr;
- return;
- break;
- continue;
The curly braces are mandatory and part of the control statement.
The
continue statement short-circuits the loop
and immediately jumps to the loop start. Continue in for() loops will execute
the increment part of the loop prior to testing the condition. Loops may be
broken by
return and
break
statements. A
return without value/expr returns
a variable that will return true on test isundef(). See functions below.
if()/elif()/else
Conditionals start with an if() clause and may include as many elif() clauses
as you need. Optionally they may end with an else clause. The arguments to if()
and elif() are evaluated to boolean expressions. Examples:
if(!isvector(val)) {
error("val should be a vector");
return;
}
if(value >= 43) {
} elif(value < 0) {
} elif(value == 5) {
} else {
}
foreach()
The foreach() construct expects two semi-colon separated arguments where the
first is a vector or vector-list type and the second an identifier
(loop-variable). The identifier is assigned a
copy of each vector from
the vector-list, or scalar from the vector, before executing the loop content.
The loop-variable may contain en empty vector or undef if the source
vector-list or source vector contains them. Example:
list = { [0, 0], [1, 0], [1, 1], [0, 1] };
foreach(list; v) {
move(v);
}
foreach([1, 7, 3, 6, 4, 3, 1, 9, 0, 6, 4]; i) {
complex_sequenced_func(i);
}
for()
The for() construct expects three semi-colon separated arguments where the fist
indicates the initialization, the second is the loop condition and the third
argument an increment statement. Both the initialization and the increment
statement may be omitted, in which the for() behaves exactly like a while()
loop. Example:
for(i = 0; i < 10; i++) {
do_something(i);
}
while()
The while() construct expects one arguments that functions as the loop
condition. The loop repeats for as long as the condition evaluates to true.
Example:
while(complex_algo_check()) {
complex_algo_update(arg1, arg2);
}
do/while()
The do/while() construct expects one arguments that functions as the loop
condition. The loop repeats for as long as the condition evaluates to true and
executes
at least once. The while() clause must be terminated with a
semicolon. Example:
do {
res = complex_algo_update(arg1, arg2);
} while(res > limit);
repeat()
The
repeat construct is a simple loop that
repeats the loop a number of times indicated by the first argument, which must
result in an integer scalar value. Both positive and negative repeats are
allowed. The number of loops in a repeat is the absolute value of the scalar.
An optional second argument to repeat exposes the loop-variable, which will
count 1, 2, 3,... for positive repeats and -1, -2, -3,... for negative
repeats.
Repeat loops should specify a scalar of integer value and no units associated.
NOTE: A floating point scalar to indicate the number of loops is bound to
EPSILON calculation and is considered to be of integer value when the nearest
integer is within EPSILON. I.e. a loop count will be
n for all values
n-EPSILON < n < n+EPSILON. A warning will be
issued outside this range and the scalar will be truncated. Example:
repeat(5) {
move_r([-, 1]);
dwell(0.5);
}
repeat(10; i) {
move([val * i / 10.0]);
...
}
Functions can be defined anywhere in the source. They have the following format:
function name(optargs) { ... }
Any defined variables in functions are local by default unless a global
variable of same name already exists. Variables can be forced to be in the
local scope when declared as local before use. Explicitly declared local
variables will hide the global counterpart if it exists.
The
local keyword allows for multiple variables
to be declared local in a comma separated list. Each local declaration may
additionally have an assignment.
function blabla() {
local foo, bar;
local var = 123, val = 0;
...
const localconstant = 999;
}
Variables declared constant inside a function are also entered into the local
scope and will cease to exist after the function returns.
Function return
Functions may return a value using the
return
statement. The function will terminate immediately when a return is executed. A
return without argument effectively returns an <undef> value. Trying to
assigns a value from a function that did not use a return statement to exit the
function results in a runtime error.
function one()
{
return 1;
}
function nothing()
{
return;
}
message("one(): ", one());
message("nothing(): ", nothing());
Function arguments by reference
All arguments passed to functions are passed by value by default. Passing by
reference is possible when the function definition marks the appropriate
parameter with an ampersand (&). Example:
function func(valval, &valref)
{
valref *= 10;
valval *= 10;
}
i = 1;
j = 1;
func(i, j);
message("i=", i, ", j=", j);
It should be noted that passing large vectorlists as values is slower than
passing them by reference. If you use (very) large vectorlists with many
(large) vectors, then passing them by reference may speed up your application.
The definition of "large vectorlists" is somewhat arbitrary, but you should
consider it when you have 50..100 vectors in a list being passed in deeply
nested function calls.
Default function arguments
Function arguments may be setup with default values. All following arguments in
the definition of a function must include default values once an argument has
been assigned a default value. The default may be an arbitrary expression.
function defargfunc(arg, defarg1 = 123, defarg2 = [1, sin(45.0deg)])
{
comment(arg, " ", defarg1, " ", defarg2);
}
defargfunc(1, 2, 3);
defargfunc(1, 2);
defargfunc(1);
Using default arguments does not check the type of the argument in the call. In
other words, the type of the argument may change depending whether or not the
argument was passed in the call or not. You can query the actual type using
the
is*() functions.
A slightly more useful example:
SAFEZ = [-, -, 10.0];
CUTZ = [-, -, -5.0];
HOME = [0, 0];
function do_stuff(vecs)
{
local v;
goto(vecs[-1]);
move(CUTZ);
foreach(vecs; v) {
move(v);
}
goto(SAFEZ);
}
list = { [0,0], [1,0], [1,1], [0,1] };
do_stuff(list);
goto(HOME);