1.6 Diving InNo doubt there are readers who are eager to dive into AppleScripting before they go on to this book's upcoming language reference. This section summarizes the important AppleScript language elements you need to know before you start coding:
All of these language elements are described in more detail in Part II (except for case sensitivity and statement termination, which are taken care of adequately in the following sections). 1.6.1 Case SensitivityUnlike other scripting languages such as JavaScript and Perl, AppleScript is not case-sensitive. In other words, MYVAR is the same as myvar, or myfunc is the same as MyFunc in terms of function definitions. Script Editor will not let you define two functions with the same name, even if their letters are different combinations of upper- and lowercase characters. The numerous AppleScript constants and reserved words (case, current application, and other constants are covered in Chapter 6) cannot be reused as your own variable or method names. A script can change the values of predefined variables such as pi or space; however, scripters are better off using these predefined variables for the variables' intended purpose and creating their own variable names. Script Editor sees "pi" and "PI" as the same thing ("PI" would be corrected to "pi" when you compile the script). Class and command names within applications, while mostly lowercase, are corrected when you compile the script to the spelling that is specified in the app's dictionary. (Chapter 2 explains an application's dictionary.) For instance, if you typed the class name tcpip v4 configuration into Script Editor, inside of a tell app "Network Setup Scripting" block, it would be corrected to "TCPIP v4 configuration" when the statement was compiled. 1.6.2 Statement TerminationYou don't have to terminate an AppleScript statement using any special characters, as you do with Perl (the semi-colon character). You do, however, have to complete each statement on a line before you go on to the next statement, unless you use the continuation character (¬ ). 1.6.3 Line Continuation CharacterYou can split a very long statement into several lines by typing Option-Return on the Macintosh. This produces a continuation character (¬). This character only affects how the code looks in Script Editor and is not part of the compiled code. If you store a string literal in a variable, however, and add a continuation character to the middle of the literal string, then this character becomes a visible part of the compiled string of characters (you usually want to avoid this). Splitting long code statements with the continuation character makes the script more readable. You will use this character often. 1.6.4 Naming Identifiers or Variables and FunctionsThe names that you create for variables and functions have to begin with a letter or underscore character ( _ ), but subsequent characters can include letters, numbers, and underscores. In variables or function names, you cannot include AppleScript's reserved words and operators such as *, &, ^, or + (covered in Chapter 4) or special characters such as $, @, or #. An exception to this AppleScript rule allows for the creation of weird variable or function names if you use vertical bars (|) to begin and end the identifier, as in: set |2^$var| to 25. The variable |2^$var| is actually valid. If you wanted to create the equivalent of a Perl scalar variable in AppleScript, you could use: set |$perlVar| to 25. There is no practical limit to the size of AppleScript variable names; that is, you can have a variable name that has up to 251 characters, but you would never want to deal with variable names that long. In my OS 9 testing, a variable name that exceeded 251 characters produced the error dialog in Figure 1-10. Figure 1-10. Script Editor signals a variable name that's too long1.6.5 Variable DeclarationYou can use either the set or copy keywords to declare a variable and assign a value to it, as in the following examples: set myvar to (5 * 25) copy (5 * 25) to myvar Both of these statements produce the same result; they store the integer 125 in the myvar variable. The set version however is more intuitive and is used more often to declare variables. set and copy furthermore have different results when you use the variable to contain a list, record, or script object. (Chapter 9, discusses this AppleScript feature.) The copy keyword, as in: copy listVar to newListVar will make a new copy of the list value stored in listVar and store this new list in newListVar. If you used: set newListVar to listVar the list stored in newListVar will still refer to the original list (i.e., listVar). The newListVar variable will not get a new copy of the list when you use set. Chapter 6 goes further into this set and copy subject. 1.6.6 CommentsComments are the descriptions that you add to the code as reminders to yourself and guidance to other coders; they are not part of the executable script. AppleScript uses two or more dashes (—) preceding the comment text for single-line comments and (* *) surrounding the comment text for single- or multi-line comments. Using dashes, you can have a comment on the same line as some code, such as: set myvar to 10 -- initialize myvar to 10. AppleScript does not use the popular slash-slash (//) single-line comment characters of Java or C++. 1.6.7 Data TypesLike most scripting tools, AppleScript is a "loosely typed" programming language. This means that for the most part you do not have to specify exactly how the computer will store some data when you set a variable to a value. AppleScript takes care (or tries to) of the details for you. So when you use the code fragment: [set num to 75], AppleScript knows that num is an integer or number. If you use: set numstr to "I'm a string" numstr is automatically stored as a string. This feature does not forbid you from specifying the data type of a variable, which is a good idea in many situations and creates more readable code. If you want to explicitly set a variable to an AppleScript data type, use the as keyword, as in get current date as string. If you want to ensure that a number will be stored as a real data type, use code such as: set num to 75.0 This code sets the variable num to a real data type, which is a very large number that can include a decimal point, similar to a double type in Java. A program can now increment or increase the variable num to a much higher number than it could if it were left as an integer, which has a range of -536,870,911 to 536,870,911, inclusive. What if you wanted to have a variable keep track over time of the number of people on Earth who are connected to the Web? This number would eventually exceed one billion, so you would want to use a variable of a real data type. However, explicitly setting data types in AppleScript is also a potentially error-prone strategy if you are not careful in your script planning. For example, the code: set num to 1.5 as integer will compile but raise an error once the script is run. The error message is "Can't make 1.5 into an integer." If you left the as integer part out of the code fragment, then AppleScript would automatically set num to a real data type and no error would occur. The following list briefly describes the other principal data types that AppleScripters should be aware of (these are all covered in more detail in Chapter 3):
Example 1-8. Boolean Values in AppleScriptset bool to false -- bool is a boolean data type set bool to (5 > 3) (* bool is a boolean because the expression "(5 > 3)" evaluates to true *)
1.6.8 Operators and Reference FormsAn operator is a symbol or token that is used with values or variables in an AppleScript expression. An example is the well-worn expression 2 + 2 = 4 (if you just dropped this expression into a Script Editor window, it would return a boolean value of true). The operators in this expression are "+" and "=". AppleScript has most of the operators that you would expect a scripting language to make available to the programmer. AppleScript also allows the scripter to use very readable English expressions for operators, such as: if 5 is greater than 3 and 6 equals 6 then set bool to true The principal symbolic operators are demonstrated in Example 1-10. All operators, including the English forms, are described in Chapter 4. Example 1-10. AppleScript Operators(* & concatenates one string to another, or combines two or more lists or records *) set twoPhrases to "One phrase " & "connected to another phrase." (* the following code returns {"a string inside of a list", "added at the end of a sentence."} *) set twoLists to {"a string inside of a list"} & {"added at the end of a sentence."} (* & also combines two records to make one record. *) set twoRecs to {firstn:"Amanda"} & {secondn:"Smith"} (* parentheses and Math operators do what you would expect them to *) set int to (5 * 6) - 8 -- returns 22 (*If you use / or ÷ the result is always a real data type. If you use div the result is always an integer *) set n1 to 50 / 26 -- returns 1.923076923077 set n2 to 50 ÷ 26 -- returns 1.923076923077 set n3 to 50 div 26 -- returns 1; div only returns integer data types (* < > = are used to test equality *) set bool to 50 < 26 -- bool is false set bool to 50 > 26 -- bool is true set bool to 50 = 26 -- bool is false set bool to 50 26 -- bool is true (* ^ is the exponentiation operator *) set n1 to 50 ^ 2 -- n1 is 2500.0, a real data type (* mod returns the fractional part and throws out the rest of the integer part, the opposite of div which throws out the fractional part *) set n2 to 7 mod 3 -- n2 is 1 (* not, or, and are boolean operators; they are used to combine two expressions to produce a boolean result *) set bool to true and false -- bool is false set bool to true or false -- bool is true set bool to not true -- bool is false set bool to (2 + 2 = 4) and (not (2 ^ 2 = 4)) (* you can combine expressions to get a result; bool is false in this case because the second part of the expression (i.e., (not (2 ^ 2 = 4)) ) evaluates to false *) A reference form is an English or symbolic expression that describes where a value is within its container. As an AppleScripter, you will often find yourself describing contained objects in order to accomplish your task, such as "get the first record in the database named myDB" or, "get the second paragraph of the last document in the folder named January Stuff." AppleScript offers numerous ways to refer to these contained items. Chapter 4 goes into great detail in describing these methods, which are demonstrated in short by Example 1-11. For example, you can describe contained items by referring to the first-tenth item, then with anything that requires a reference that exceeds 10 you use the number, as in: get the 1000th word of document "Mydocument". Example 1-11. Different Ways to Refer to Contained Itemstell application "Finder" to get the folder after (the 20th folder of startup disk) (* gets the folder object after the 20th folder on the startup disk *) tell application "Finder" to get the folder after the 20th folder of¬ startup disk tell application "Finder" to get the folder before the 20th folder of¬ startup disk You can create very useful and detailed scripts using AppleScript's numerous reference forms. Example 1-12 gets only one part of a list of numbers (i.e., the numbers 3 through 6) and stores this sub-section in another list variable. The following section briefly describes the repeat and if...end if statements that appear here. Example 1-12. Using the Range Reference Formset list1 to {1, 2, 3, 4, 5, 6} set list2 to (numbers 3 thru 6 in list1) -- returns only {3,4,5,6} 1.6.9 Flow-Control StatementsAppleScript includes syntax that you can use to test expressions and only have some code execute if certain conditions are met, as well as loop through code statements and then exit from the loop. These programming constructs are often called flow-control statements. These statements are covered extensively in Chapter 7. Like many other programming languages, AppleScript uses an if...then...else...end if statement to test various expressions. AppleScript also has an if simple statement and an if compound statement (one that extends over several lines). For example, you can use code such as: if numVar > 1000 then return 1000 on one line without including any end ifs. Example 1-13 includes both types of if statements. AppleScript uses several variations of the repeat statement to loop through code (see Example 1-13). Repeat is AppleScript's version of the for (;;;) or while... or foreach... loops of other languages like Perl or Java. AppleScripters often use repeat to loop through the values of a list. For example, the code: repeat with m in listVar...end repeat will loop through all of the list members stored in the variable listVar. Within the loop, the iterator variable m will sequentially contain each list-member value. Example 1-13 includes several versions of the repeat and if...end if statements. Example 1-13. AppleScript if and repeat Statementsset userNum to 75.66 (* compound if statement *) if the class of userNum is real then display dialog "It's a real number." else if the class of userNum is integer then display dialog "It's an integer." else (* you can include a final else as part of the test; in this case it is not necessary *) end if (* simple if statement *) if userNum is greater than 100 then display dialog "The value exceeds 100." (* various repeat loop variations *) repeat 10 times set userNum to userNum + 1 end repeat (* endless loop without 'exit repeat' *) repeat exit repeat -- this loop only iterates once because of exit end repeat repeat while userNum < 10000 set userNum to userNum * 2 end repeat (* if the statement following 'until' evaluates to 'true' then the repeat statements are not executed *) repeat until userNum >100000 exit repeat (* this exits from this loop right away; you could do something else if you wanted to *) end repeat set myList to {1,2,3,4} (* repeat statement that iterates over list contents *) repeat with mem in myList set userNum to userNum + mem end repeat (* A different kind of repeat loop *) repeat with loopVar from 2 to 10 by 2 (* loop circles five times; loopVar is 2,4,6,8,10 *) --do something here end repeat Another important statement construct in AppleScript provides the language with error-trapping capability. It is called the try statement, and looks like try...on error...end try. try is similar to Java's try...catch( ) statement. If you enclose a series of AppleScript statements in a try block and one of them raises an error, AppleScript will "catch" the error and allow you to deal with it in a responsible manner. Without using a try statement, a run-time error will cause a script to display an error message, and then terminate the script execution. Example 1-14 demonstrates how to use the try block. Again, Chapter 7 thoroughly describes these statements and others. Example 1-14. Trapping Errors with trytry set aNum to (text returned of (display dialog¬ "Enter a number." default answer "")) set aNum to aNum as real on error errmsg display dialog "It looks like you did not enter a number: " & errmsg end try 1.6.10 SubroutinesSubroutines are code units that can be used over and over again throughout the script once they are defined in the AppleScript program. They are essentially user-defined commands. Subroutines or handlers in AppleScript can be called with or without parameters, similar to functions in other languages. The subroutine can return a value to the calling script or simply perform a task and exit without returning a value. You can do almost whatever you want in a subroutine (except define another subroutine within it), including declare and initialize variables and call other functions. Example 1-15 creates a simpler way of producing a character from its ASCII decimal number equivalent. It calls the ASCII character scripting addition to produce the value. Example 1-15. Simple AppleScript User-Defined Subroutineset let to chr(67)-- the variable is set to 'C' on chr(int) return ASCII character int end chr To define a subroutine in AppleScript, use the keyword on followed by the subroutine name, an opening parenthesis character, one or more optional variables that represents any subroutine arguments, and a closing parenthesis. If you have more than one parameter, then separate them with commas. Subroutines that do not have any parameters require empty parentheses, as in on chr( ). The end chr part, or simply end, is also required (Script Editor will automatically add the name of the subroutine after end if you forget to do it yourself). Whatever you want the subroutine to do is defined within the on char( )...end block. A return statement will immediately return from the subroutine, and it will optionally return a value, as in return "finished" (the subroutine would return a string "finished"). You can pass objects (such as dates) to a subroutine as parameter values. Example 1-16 returns the difference in days between two dates. Example 1-16. Getting the Difference Between Two Datesset dayDiff to getDiff(current date, date "Sunday, January 1, 1984 12:00:00 AM") on getDiff(date1, date2) if date1 > date2 then return ((date1 - date2) / (24 * 60 * 60)) else (* if the dates are equal then the subroutine returns 0.0 *) return ((date2 - date1) / (24 * 60 * 60)) end if end getDiff Subtracting one AppleScript date object from another returns the difference between the two dates in seconds. This subroutine processes the seconds to return the difference in days (the result is a real data type, so it might look like 6185.835706018518, which is almost 6,186 days). Example 1-15 and Example 1-16 both show that you can call a subroutine higher up in a script than where its definition appears. 1.6.11 Script ObjectsScript objects give AppleScript very basic object-oriented features, including inheritance. A script object is defined in a script-code block that looks a little like a subroutine definition. Script objects are created within a script with the script [script name]...end script syntax. Example 1-17 contains a simple script object definition. The object has two methods: one returns a property value and the other method increments the value of the property by one. The bottom of the script creates two copies of this object then calls its methods and displays the results. Example 1-17. Creating a Script Object(* begin the script object definition *) script Test property myval : 0 -- one integer property on getVal( ) -- define a method return myval -- return the prop value end getVal on upVal( ) -- define another method set myval to myval + 1 -- increment myval property by one end upVal end script -- end script object definition copy Test to t1 -- create new Test object copy Test to t2 -- create another, different Test object (* two ways to call an object's methods *) tell t1 to upVal( ) t2's upVal( ) t2's upVal( ) (* t2's upVal method is called twice, setting its myval property to 2 *) set theMessage to "t1: " & (t1's getVal( ) as string) (* find out the two object's property values *) set theMessage to theMessage & return & "t2: " & (t2's getVal( ) as string) display dialog theMessage You may have noticed the use of the keyword copy to create the two Test objects in Example 1-17. The copy keyword creates a new copy of the object and stores it in the named variable, as in [copy Test to t1]. If the script used set t1 to Test and set t2 to Test, then the variable t2 would not have a new copy of the Test object. It would refer to the same Test object (and the same myval property) as the t1 variable. Libraries present a real-world use of script objects. A library, which is a type of script object, can be one or more method definitions stored as a compiled script. To use the library's methods, load it into the script with the load script scripting addition (Example 1-18 and Example 1-19 demonstrate this). Example 1-18. Defining a Library(* define some methods and save as an applet or compiled script; the file name of the script is "farewell" *) on sayGoodbye( ) display dialog "Goodbye" end sayGoodbye on sayCiao( ) display dialog "Ciao" end sayCiao Now load the library into any old script by providing its file path (where the script is saved on the computer) as a parameter to the load script scripting addition. Example 1-19. Using a Libraryset bye to load script "macintosh hd:desktop folder:farewell" (* call library methods *) bye's sayGoodbye( ) bye's sayCiao( ) Chapter 9 goes into much greater depth on script objects. |