Macro user guide
This example shows you how to use the PowerMill macro programming language to create a macro which prints the words of the counting song "Ten Green Bottles".
- Basic macro
- Adding macro variables
- Adding macro loops
- Running macros with arguments
- Adding your own functions
- Decision making in macros
- More on functions in macros
- Using the SWITCH statement
- Returning values from macros
- Using a FOREACH loop in a macro
- Using arrays in a FOREACH loop
- Pausing a macro for user interaction
Basic macro
This shows you how to create and run a basic macro using PowerMill's programming language.
In a text editor such as WordPad enter:
PRINT "10 green bottles sitting on the wall" PRINT "10 green bottles sitting on the wall" PRINT "And if 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall"
Save the file as example.mac.
In PowerMill, click View tab > Window panel > User Interface > Command Window.
From the Macro context menu, select Run. This displays the Select Macro to Run dialog.
Move to the appropriate directory, select example.mac, and click Open. The macro runs and the command windows displays the text enclosed in quotations marks (") in the macro.
Adding macro variables
The first two lines of example.mac are the same. To minimise repetition (and for ease of maintenance) it is good practise to write the line once and recall it whenever it is needed. To do this you must create a local variable to hold the line of text.
You can create different types of variables in PowerMill. To store a line of text you must use a STRING variable.
Open example.mac in your text editor and change it to:
// Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" PRINT $bottles PRINT $bottles PRINT "And if 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall"
Note: The first line is a comment which explains the second line.Save the file as example.mac.
In PowerMill, Run the Macro. The command windows displays the same as before:
You should be aware of several issues with variables:
You must define all local variables before they are used, in this case
STRING bottles = "10 green bottles sitting on the wall"
defines the local variable bottles.The variable bottles is a local variable, so is only valid within the macro where it is defined. It is not a PowerMill variable. Typing it into the command window gives an error.
When you have defined a local variable you can use it as many times as you want in a macro.
You can define as many local variables as you want in a macro.
Adding macro loops
There are two lines of the macro which are the same: PRINT $bottles
. This is acceptable in this case because the line only appears twice, but if you wanted to repeat it 5 or 20 times it would be better to use a loop. PowerMill has three looping statements:
This example uses the WHILE statement to repeat the command 5 times.
Open example.mac in your text editor and change it to:
// Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" // Create a variable to hold the number of times // you want to print the first line. // In this case, 5 INT Count = 5 // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } // Print the last two lines PRINT "And if 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall"
Note:$Count = Count - 1
is an assignment statement which is why the variable ($Count
) to the left of = must be prefixed with$
.Note: The empty lines are not necessary, but they make it easier to read the macro.Save the file as example.mac.
In PowerMill, Run the Macro. The command windows displays:
]
Note: ChangingINT Count = 5
toINT Count = 10
prints 10 green bottles sitting on the wall ten times, rather than five.
Running macros with arguments
The loop you added to example.mac works well if you always want to print 10 green bottles sitting on the wall the same number of times. However, if you want to change the number of repetitions at run time, rather than editing the macro each time, it is much better to write the macro so it is given the number of repetitions. To do this you need to create a Main FUNCTION.
Open example.mac in your text editor and change it to:
// Create a Main FUNCTION to hold the number of times // you want to print the first line. FUNCTION Main (INT Count) { // Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } // Print the last two lines PRINT "If 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall" }
Save the file as example.mac.
To run the macro you cannot select Run from the Macro context menu, as you need to give a value for Count. Therefore, in the command window type:
MACRO example.mac 5
Where 5 is the value for Count. The command windows displays:
Note: If you get a warning that the macro cannot be found, check you have created the necessary macro path.
Adding your own functions
As well as a Main function you can create your own functions. This is useful as a way of separating out a block of code. You can use functions:
to build up a library of useful operations
to make a macro more understandable.
This example separates out the printing of the first line into its own function so that the Main function is more understandable.
Open example.mac in your text editor and change it to:
FUNCTION PrintBottles(INT Count) { // Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT \$bottles // Reduce the count by 1 $Count = Count - 1 } } FUNCTION Main (INT Count) { // Print the first line Count number of times CALL PrintBottles(Count) // Print the last two lines PRINT "If 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall" }
Save the macro.
Run the macro by typing MACRO example.mac 5 in the command window.
This produces the same result as before.
Decision making in macros
The macro example.mac runs provided that you enter a positive argument. However, if you always want the 10 green bottles sitting on the wall line printed at least once use:
A DO - WHILE loop as it executes all the commands before testing the conditional expression.
An IF statement.
DO - WHILE loop
Edit the PrintBottles function in example.mac to:
FUNCTION PrintBottles(INT Count) { // Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 DO { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } WHILE Count \> 0 }
The main function remains unchanged:
FUNCTION Main (INT Count) { // Print the first line Count number of times CALL PrintBottles(Count) // Print the last two lines PRINT "And if 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall" }
Type MACRO example.mac 0 in the command window.
The 10 green bottles sitting on the wall line is printed once.
IF statement
You can use an IF statement to ensure the 10 green bottles sitting on the wall line is printed at least twice.
Edit the Main function in example.mac to:
FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } // Print the first line Count number of times CALL PrintBottles(Count) // Print the last two lines PRINT "And if 1 green bottle should accidentally fall" PRINT "There will be 9 green bottles sitting on the wall" }
The PrintBottles function remains unchanged:
FUNCTION PrintBottles(INT Count) { // Create a variable to hold the first line STRING bottles = "10 green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } }
Type MACRO example.mac 0 in the command window.
The 10 green bottles sitting on the wall line is printed twice.
More on functions in macros
So far you have only printed the first verse of the counting song "Ten Green Bottles". To make your macro print out all the verses you must change the PrintBottles function so it takes two arguments:
Count for the number of times "X green bottles" is printed.
Number for the number of bottles.
Edit the PrintBottles function in example.mac to:
FUNCTION PrintBottles(INT Count, INT Number) { // Create a variable to hold the first line STRING bottles = String(Number) + " green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } }
This adds a second argument to the PrintBottles function. It then uses a parameter function to convert the Number to a string value, STRING (Number). It is then concatenated (+)with green bottles sitting on the wall to make up the bottles string.
Edit the Main function in example.mac to:
FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } // Start with ten bottles INT Bottles = 10 WHILE Bottles > 0 { // Print the first line 'Count' number of times CALL PrintBottles(Count, Bottles) // Count down Bottles $Bottles = $Bottles - 1 // Build the number of 'bottles_left' string STRING bottles_left = "There will be " + string(Bottles) + " green bottles sitting on the wall" // Print the last two lines PRINT "If 1 green bottle should accidentally fall" PRINT $bottles_left } }
Type MACRO example.mac 2 in the command window.
Using the SWITCH statement
So far you have used numerals to print the quantity of bottles but it would be better to use words. So, instead of printing 10 green bottles … print Ten green bottles ….
One way of doing this is to use a large IF - ELSEIF chain to select the text representation of the number. Another way is to use the SWITCH statement.
SWITCH Number {
CASE 10
$Text = "Ten"
BREAK
CASE 9
$Text = "Nine"
BREAK
CASE 8
$Text = "Eight"
BREAK
CASE 7
$Text = "Seven"
BREAK
CASE 6
$Text = "Six"
BREAK
CASE 5
$Text = "Five"
BREAK
CASE 4
$Text = "Four"
BREAK
CASE 3
$Text = "Three"
BREAK
CASE 2
$Text = "Two"
BREAK
CASE 1
$Text = "One"
BREAK
DEFAULT
$Text = "No"
BREAK
}
The switch statement matches the value of its argument (in this case Number) with a corresponding case value and executes all the subsequent lines until it encounters a BREAK statement. If no matching value is found the DEFAULT is selected (in this case No).
Returning values from macros
This shows you how to create an OUTPUT variable from a SWITCH statement.
Create a new function called NumberStr containing the SWITCH statement in Using the SWITCH statement and a first line of:
FUNCTION NumberStr(INT Number, OUTPUT STRING Text) {
and a last line of:
}
Edit the PrintBottles function in example.mac to:
FUNCTION PrintBottles(INT Count INT Number) { // Convert Number into a string STRING TextNumber = '' CALL NumberStr(Number,TextNumber) // Create a variable to hold the first line STRING bottles = TextNumber + " green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } }
This adds the OUTPUT variable to the PrintBottles function.
Edit the Main function in example.mac to:
FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } // Start with ten bottles INT Bottles = 10 WHILE Bottles > 0 { // Print the first line Count number of times CALL PrintBottles(Count, Bottles) // Countdown Bottles $Bottles = $Bottles - 1 // Convert Bottles to string STRING BottlesNumber = '' CALL NumberStr(Bottles, BottlesNumber) // Build the number of bottles left string STRING bottles_left = "There will be " + lcase(BottlesNumber) + " green bottles sitting on the wall" // Print the last two lines PRINT "If one green bottle should accidentally fall" PRINT $bottles_left } }
The BottlesNumber variable is declared in the WHILE loop of the MAIN function.
Note: Each code block or function can define its own set of local variables; the scope of the variable is from its declaration to the end of the enclosing block or function.Add the NumberStr function into example.mac.
FUNCTION PrintBottles(INT Count, INT Number) { // Convert Number into a string STRING TextNumber = '' CALL NumberStr(Number,TextNumber) // Create a variable to hold the first line STRING bottles = TextNumber + " green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } } FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } // Start with ten bottles INT Bottles = 10 WHILE Bottles > 0 { // Print the first line Count number of times CALL PrintBottles(Count, Bottles) // Countdown Bottles $Bottles = $Bottles - 1 // Convert Bottles to string STRING BottlesNumber = '' CALL NumberStr(Bottles, BottlesNumber) // Build the number of bottles left string STRING bottles_left = "There will be " + lcase(BottlesNumber) + " green bottles sitting on the wall" // Print the last two lines PRINT "If one green bottle should accidentally fall" PRINT $bottles_left } } FUNCTION NumberStr(INT Number, OUTPUT STRING Text) { SWITCH Number { CASE 10 $Text = "Ten" BREAK CASE 9 $Text = "Nine" BREAK CASE 8 $Text = "Eight" BREAK CASE 7 $Text = "Seven" BREAK CASE 6 $Text = "Six" BREAK CASE 5 $Text = "Five" BREAK CASE 4 $Text = "Four" BREAK CASE 3 $Text = "Three" BREAK CASE 2 $Text = "Two" BREAK CASE 1 $Text = "One" BREAK DEFAULT $Text = "No" BREAK } }
To run the macro, type MACRO example.mac 2 in the command window.
Using a FOREACH loop in a macro
This example shows you how to use a FOREACH loop to control the number of bottles rather than a WHILE loop.
Edit the Main function in example.mac to:
FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } FOREACH Bottles IN {10,9,8,7,6,5,4,3,2,1} { // Print the first line Count number of times CALL PrintBottles(Count, Bottles) // Countdown Bottles $Bottles = $Bottles - 1 // Convert Bottles to string STRING BottlesNumber = '' CALL NumberStr(Bottles, BottlesNumber) // Build the number of bottles left string STRING bottles_left = "There will be " + lcase(BottlesNumber) + " green bottles sitting on the wall" // Print the last two lines PRINT "If one green bottle should accidentally fall" PRINT $bottles_left } }
The rest of example.mac remains unaltered.
FUNCTION PrintBottles(INT Count, INT Number) { // Convert Number into a string STRING TextNumber = '' CALL NumberStr(Number,TextNumber) // Create a variable to hold the first line STRING bottles = TextNumber + " green bottles sitting on the wall" // Repeat while the condition Count is greater than 0 WHILE Count > 0 { // Print the line PRINT $bottles // Reduce the count by 1 $Count = Count - 1 } } FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } FOREACH Bottles IN {10,9,8,7,6,5,4,3,2,1} { // Print the first line Count number of times CALL PrintBottles(Count, Bottles) // Countdown Bottles $Bottles = $Bottles - 1 // Convert Bottles to string STRING BottlesNumber = '' CALL NumberStr(Bottles, BottlesNumber) // Build the number of bottles left string STRING bottles_left = "There will be " + lcase(BottlesNumber) + " green bottles sitting on the wall" // Print the last two lines PRINT "If one green bottle should accidentally fall" PRINT $bottles_left } } FUNCTION NumberStr(INT Number, OUTPUT STRING Text) { SWITCH Number { CASE 10 $Text = "Ten" BREAK CASE 9 $Text = "Nine" BREAK CASE 8 $Text = "Eight" BREAK CASE 7 $Text = "Seven" BREAK CASE 6 $Text = "Six" BREAK CASE 5 $Text = "Five" BREAK CASE 4 $Text = "Four" BREAK CASE 3 $Text = "Three" BREAK CASE 2 $Text = "Two" BREAK CASE 1 $Text = "One" BREAK DEFAULT $Text = "No" BREAK } }
To run the macro, type MACRO example.mac 2 in the command window.
This gives exactly the same output as the Returning values from macros example. It shows you an alternative way of creating the same output.
Using arrays in a FOREACH loop
This example shows you how to use an array in a FOREACH loop, rather than using a list, to control the number of bottles.
Edit the Main function in example.mac to:
FUNCTION Main (INT Count) { // Make sure that Count is at least two IF Count < 2 { $Count = 2 } // Define an array of bottle numbers INT ARRAY BottleArray[10] = {10,9,8,7,6,5,4,3,2,1} FOREACH Bottles IN BottleArray { // Print the first line Count number of times CALL PrintBottles(Count, Bottles) // Count down Bottles $Bottles = $Bottles - 1 // Convert Bottles to string STRING BottlesNumber = '' CALL NumberStr(Bottles, BottlesNumber) // Build the number of bottles left string STRING bottles_left = "There will be " + lcase(BottlesNumber) + " green bottles sitting on the wall" // Print the last two lines PRINT "If one green bottle should accidentally fall" PRINT $bottles_left } }
The rest of example.mac remains unaltered.
Type MACRO example.mac 2 in the command window.
This gives exactly the same output as the Returning values from macros example. It shows you an alternative way of creating the same output.
Pausing a macro for user interaction
You can pause a running macro to allow user input, such as the selection of surfaces or curves. The command to do this is:
MACRO PAUSE "User help instructions"
This displays a dialog containing the supplied text and a button to allow the user to RESUME
the macro.
When the macro is paused, users can perform any actions within PowerMill, with the exception of running another macro. The current macro remains paused until the user clicks the RESUME
button. If the user closes the dialog, by clicking the dialog close icon , this ends any currently running macros, including the paused macro.
For example:
GET EXAMPLES 'cowling.dgk'
ROTATE TRANSFORM TOP
CREATE TOOL ; BALLNOSED
EDIT TOOL ; DIAMETER 10
EDIT BLOCK RESET
CREATE BOUNDARY ; SELECTED
STRING Msg = "Select surfaces for boundary, and press"+crlf+"RESUME when ready to continue"
EDIT BLOCK RESET
MACRO PAUSE $Msg
EDIT BOUNDARY ; CALCULATE
If you do not enter a string after MACRO PAUSE
the macro pauses but does not display a RESUME
dialog. To resume the macro either type MACRO RUN
or provide another mechanism to continue the macro.