Calculations are performed with unary and binary operators with following precedence:
Rank  Operator  Evaluation  Description 
1  lefttoright  parenthesis 
2  lefttoright  vector/vectorlist index, vector field, postincrement, postdecrement 
3  righttoleft  unary: logical not, binary not, plus, minus, preincrement, predecrement 
4  lefttoright  multiplication, division, modulo 
5  lefttoright  binary plus, minus, plusor and minusor 
6  lefttoright  shift left, shift right 
7  lefttoright  comparison 
8  lefttoright  comparison 
9  lefttoright  binary and 
10  lefttoright  binary xor 
11  lefttoright  binary or 
12  lefttoright  logical and 
13  lefttoright  logical or 
14  righttoleft  ternary/conditional expression 
15  righttoleft  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 lefthand side) 
scalar  &  scalar  →  scalar 
vector  &  vector  →  vector (replace values on lefthand 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 unitconversion 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 1e12 of
each other. The value 1e12 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.0e12) == a
(a + 0.9e12) == a
The value 1e12 allows for femtometer accuracy and is four..five orders of
magnitude better than the covalent bondradius 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:
nEPSILON < 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.9e12);
to_int(1.0  0.9e12);
to_int(1.0 + 1.0e12);
to_int(1.0  1.0e12);
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 0x) 
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/vectorlist. 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 sideeffects
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 lefthand
side as zero if it is undef, whereas exclusive add/subtract treats operations
on lefthand side undef values as undef result. The difference between the
operator's functionality can be illustrated as follows:
Exclusive Add (+), lefthand side undef is retained, righthand 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 righthand 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 (), lefthand side undef is retained, righthand 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 righthand 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 lefthand side and the righthand 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 (+), lefthand side undef is retained, righthand side undef substituded by 0 
[, 10] + [15, , 2]   is:  →   or: 
  [ , 10]    [ , 10, ] 
 +  [15, , 2]   +  [15, 0, 2] 
 
 →  

  [ , 10, ]    [ , 10, ] 

Inclusive Add (+), both left and righthand 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
shortcircuit evaluation. I.e. the righthand side is not evaluated if the
lefthand side of the expression predetermines 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 casesensitive comparison at Unicode characterlevel
using the wcscmp() Cfunction.
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 nonundef 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 fieldnames correspond with the axisname 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 lefthand side type, including its units. The righthand 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 returntype of the true and falseclauses. 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 unitless, then the result
will also remain unitless.
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 commandline 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 righthand side of the + operator is
scalar, vector or vectorlist. Comparing strings uses a binary Unicode
characterlevel compare, is casesensitive 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 semicolon (;).
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 
elseif 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 cascadeable assignments, and are evaluated strictly
righttoleft. 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 runtime 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 commandline 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 shortcircuits 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 semicolon separated arguments where the
first is a vector or vectorlist type and the second an identifier
(loopvariable). The identifier is assigned a
copy of each vector from
the vectorlist, or scalar from the vector, before executing the loop content.
The loopvariable may contain en empty vector or undef if the source
vectorlist 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 semicolon 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 loopvariable, 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
nEPSILON < 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);