gcmc - G-Code Meta Compiler

Syntax description

Gcmc is a script language with late binding and lazy evaluation, meaning that run-time execution uncovers variable mismatches and all expressions are first known/converted to values when they are executed.

Comments are like C/C++, where /* */ indicate block comments and // is a comment to end-of-line.

Types

Variables are typed with following available types:
undefined An undefined quantity.
integer Any whole number positive or negative.
floating point Any number containing a decimal point. Floating point numbers less than |1e-16| are considered to be zero.
scalar Any integer or floating point type is considered to be scalar.
vector A list of scalar values. Any coordinate in a vector may be undefined and a vector may be empty. Vectors may be indexed to obtain the individual scalar values. Examples:
[1, 2.0]X is integer 1, Y is floating point 2.0
[-, -, -5.0]X and Y are undefined, Z is floating point -5.0
vector-list A collection/list of vectors. Vector-lists may be empty. Vector-lists may be indexed to obtain the individual vectors. Example: { [0, 0, 0], [-, 2, 3] }
string A collection of characters delimited by double quotes ("). Standard backslash escape sequences are supported, including octal and hex escapes. Embedded nul-characters are not allowed. Example:
"This is a string with \101scapes in \x44ifferent styles\n"

Units

All scalar types can have units associated. Available units are:
mmMillimeters
inInch (25.4mm)
milMil = 1/1000 inch (0.0254mm); always converted to inch
degDegrees, 360° full circle
radRadians, 2π full circle
 No units (none)

Examples: 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 upon 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

On output, all units are converted to millimeters or inches for axes XYZ and UVW (depending -i option). Axes ABC will have units converted to degrees. All values with no units associated are treated as if they were millimeters/degrees or inches/degrees as appropriate for the respective axis depending the -i option.

Note: gcmc allows for output in imperial units (-i option). All internal calculations are adapted to default to imperial units if the -i option is specified.

Operators

Calculations are performed with unary and binary operators with following precedence:
()parenthesis
[]vector/vector-list index
! + -unary: logical not, plus and minus
++ --increment, decrement
* / %multiplication, division, modulo
+ -binary plus and minus
<< >>shift left, shift right
> < >= <= == !=comparison
&&logical and
||logical or
= += -= *= /= %= <<= >>=assignment

Floating point numbers are considered equal if they are within |1e-16| of each other.

Valid operations and result:
scalar+scalarscalar
vector+vectorvector
vectorlist+vectorvectorlist
vectorlist+vectorlistvectorlist (append)
scalar-scalarscalar
vector-vectorvector
vectorlist-vectorvectorlist
scalar*scalarscalar
scalar*vectorvector (calculated as vector * scalar)
scalar*vectorlistvectorlist (calculated as vectorlist * scalar)
vector*scalarvector
vector*vectorscalar (dot product)
vectorlist*scalarvectorlist
scalar/scalarscalar
vector/scalarvector
vectorlist/scalarvectorlist
scalar%scalarscalar
vector%scalarvector
vectorlist%scalarvectorlist
scalar<<scalarscalar
vector<<scalarvector
vectorlist<<scalarvectorlist
scalar>>scalarscalar
vector>>scalarvector
vectorlist>>scalarvectorlist

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 RHS is not evaluated if the LHS of the expression pre-determines the outcome.

Index operator [] works on both lvalue and rvalue. Index values must be scalar. 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. Vector indices greater than 9 will result in a warning. Vectorlists may be indexed to any value. Examples:
vector = [1, 2, 3];
vector[2] = 6;		/* → [1, 2, 6] */
vector[3] = vector[-1];	/* → [1, 2, 6, 6] */
vector[7] = 2;		/* → [1, 2, 6, -, -, -, -, 2] */
vlist = {};
vlist[2] = [1, 2];	/* → { [], [], [1, 2] } */
vlist[1][3] = 3.1415;	/* → { [], [-, -, -, 3.1415], [1, 2] } */
/* Rvalue may be constant expression */
myvec = {[1,2], [2,3]}[1];	/* → [2, 3] */
myval = [1, 2, 3][1];		/* → 2 */
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 << 24
6 >> 13
[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]}

Undefined values may be used in some calculations, resulting in a real value or undefined. Rules for calculating with undefined are:
undef+undefundef
undef+xx (if x is scalar, else error)
x+undefx
undef-undefundef
undef-x-x (as 0-x)
x-undefx
undef*undefundef
undef*xundef (if x is scalar, else error)
x*undefundef
undef/undefundef
undef/xundef (if x is scalar, else error)
x/undefundef
undef%undefundef
undef%xundef (if x is scalar, else error)
x%undefundef
undef<<undefundef
undef<<xundef
x<<undefx
undef>>undefundef
undef>>xundef
x>>undefx
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.

Variables and statements

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: Example:
var123 = [1, 2, 3, 4, 5];
feedrate(125.77);
_xx  = 4.5mm;
yy   = 50mil;
vec  = [_xx, yy];
list = { vec, [_xx, yy], [1, 2, 3] };
a    = vec[0];   /* a equals value of _xx (4.5mm) */
b    = list[2];  /* b equals [1,2,3] */

Flow control

Program flow is controlled by standard conditional and loop control statements: The curly braces are mandatory and part of the control statement. Conditionals may include as many elif() clauses as you need. The foreach() expects a vector-list type and assigns each vector to 'ident' before entering the loop. Loops may be broken by return, break and continue statements. A return without value/expr returns a variable that will return true on test isundef().

Functions

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. All arguments passed to functions are passed by value.
function do_stuff(vecs)
{
	local v;	/* Forces v to be local */
	foreach(vecs; v) {
		move(v);
	}
	move(SAFEZ);
	goto(HOME);
}
list  = { [0,0], [1,0], [1,1], [0,1] };
SAFEZ = [-,-,10.0];
HOME  = [0,0,0];
do_stuff(list);

Including files

A script can include other files using the include() function. The files are searched along a search path added with one or more -I options on the command-line. Gcmc always adds the current directory to the search path as last entry.
Example:
include("other-file.gcmc")