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:
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 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 | + | scalar | → | scalar |
vector | + | vector | → | vector |
vectorlist | + | vector | → | vectorlist |
vectorlist | + | vectorlist | → | vectorlist (append) |
scalar | - | scalar | → | scalar |
vector | - | vector | → | vector |
vectorlist | - | vector | → | vectorlist |
scalar | * | scalar | → | scalar |
scalar | * | vector | → | vector (calculated as vector * scalar) |
scalar | * | vectorlist | → | vectorlist (calculated as vectorlist * scalar) |
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 |
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 << 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]} |
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 | → | x (if x is scalar, else error) |
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.
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:
- break continue deg do else foreach for function if in include local mil mm rad return while
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:
- 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);
- return expr;
- return;
- break;
- continue;
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")