本文是阅读“AppleScript” William R.Cook的笔记。
该文描述了Mac OS上的通用脚本语言AppleScript和通用的进程间通讯机制Apple Events。
支持脚本的(scriptable)应用程序中的数据对象包含(子)元素(elements)和属性(properties)字段。(子)元素字段可以有多个值,这些值都是(子)对象的引用,而属性字段的值要么是一个基本类型(如字符串、整数等)要么是一个对象引用。比如一个document包含若干个paragraph子元素,且有一个name属性, 一个paragraph有style和size属性,且包含word和character子元素。
Apple Events是一个标准远程过程调用(remote procedure calls)机制的变种,应用程序可以通过发送Apple Events来互相通讯。每个event都包含一个动词(verb)和若干参数,参数可以是基本类型值或者是一个object specifier(对象描述符)。对象描述符其实是一个查询表达式,远端程序对它解释求值后得到该程序内的数据对象(集合)。因此这种机制避免了远程对象虚体(stub)的存在。比如,下面的表达式求值后就得到一个textStyle属性集合:
the textStyle of character 1 to 10 of card field "Description"
一个对象描述符一般由properties(如textStyle)和elements(如character,card field)组成,elements后头可跟名字、下标或范围以从集合中选择element(s)。
AppleScript常用于复杂任务的自动化和应用程序的定制,它提供了常用的编程设施(control flow, variables, data structures)。它的一个主要功能是发送和接收Apple Events,发送一个Apple Event类似于一个过程调用,而接收到的Apple Event的处理则是由同名的subroutine负责。AppleScript提供了被称为
object references(对象引用)的特殊语法形式来处理Apple Event的object specifiers, 它的形式定义如下:
reference ::=
| property
| `beginning'
| `end'
| `before' term
| `after' term
| `some' singularClass
| `first' singularClass
| `last' singularClass
| term (`st' | `nd' | `rd' | `th') anyClass
| `middle' anyClass
| ( pluralClass | `every' anyClass) [`from' term toOrThrough term]
| anyClass term [toOrThrough term]
| singularClass `before' term
| singularClass `after' term
| term (`of' | `in' | `'s') term
| term (`whose' | `where' | `that') term
toOrThrough ::= `to' | `thru' | `through'
call ::= message `(' expr* `)'
| message [`in' | `of'] [term] arguments
| term name arguments
message ::= name | terminologyMessage
arguments ::= ( preposition expression | flag | record)*
flag ::= (`with' | `without') [name]+
record ::= `given' (name `:' expr)*
preposition ::=
`to' | `from' | `thru' | `through'
| `by' | `on' | `into' | terminologyPreposition
在AppleScript中对象引用的值就对应于一个对象描述符。如果有个操作是针对对象引用进行的,那么相应地就会有一个包含了正确的动词(verb)和对象描述符的Apple Event创建出来。下面是几个对象引用的例子:
the first word of paragraph 22
name of every figure of document "taxes"
the modification date of every file whose size > 1024
请注意最后一个例子, 它是一个带有条件测试的对象引用。Object references很象first-class的指针类型,可以(自动)解引用,也可以通过它更改相关的值。
每个支持脚本的(scriptable)应用程序会有一个对应的应用程序术语集(application terminology),里面定义了它支持的所有event,属性和元素的名字。而AppleScript本身也有自己的术语:
application "name" 标识一个应用程序;
application "appName" of machine "machineName" 标识一个在特定机器上的应用程序;
一段脚本命令可以通过tell发给特定的应用程序:
tell application "Excel" on machine x
put 3.14 into cell 1 of row 2 of window 1
end
因为Microsoft Excel的表格术语集(spreadsheet terms)包含有诸如cell、formula这样的名词和recalculate
这样的动词,所以在语句tell application "Excel" 引入的作用域内,脚本是可以使用所有Excel术语的(这与dynamically scoping很类似),就好象它们本来就是AppleScript的固有组成部分一样,这相当于对AppleScript的基本编程概念做了针对该应用领域的(domain-specific)扩展。
AppleScript的解析器必须能够处理语言内建的设施以及应用程序的术语集。术语集可以包含由多个单词(words)组成的名字,这意味这词法分析器必须能够识别由多个单词组成的一个完整的逻辑标识符。因此,词法分析器依赖语法解析器的状态:进入一个tell作用域时,token表被扩展,离开时被重置。
AppleScript有一个简单的对象模型。一个script对象包含有属性(properties)和方法(methods)。方法是动态派发的(dynamically dispatched),所以支持了简单的面向对象编程。下面是个例子:
script Counter
property count : 0
to increment
set count to count + 1
return count
end increment
end script
方法的定义语法有点象LOGO。上面脚本执行时会将一个新的script object绑定到名字Counter上。方法的定义里还可包含script定义以创建多个实例,这样的方法就是工厂方法(factory method)。 因为script能访问的变量是词法定界(lexically scoping)的,所以一个工厂方法创建的所有对象都能访问该工厂方法所属对象的内部状态,这种机制很象Smalltalk的class/metaclass机制。AppleScript的对象模型是类似于Self那样基于原型(prototype)的,而且通过将未处理的命令委托给该对象的parent属性值来支持单继承。
一些应用程序可以通过记录用户对GUI的操作来生成相应的AppleScript程序,比如,如果用户选择File Open菜单,然后在"Personal"目录下选择一个"Resume"文件,那么相应的Apple Event将是一个path参数为"Personal:Resume"的FileOpen事件。很显然,一个Apple Event往往由多个底层事件(鼠标点击等)组成,这种记录与那些只记录鼠标、键盘等低层消息的行为比显得更高级、更抽象,而且因为每个用户操作都要能用Apple Event来描述,所以也使得记录程序复杂了许多。为此,文章提出了一种架构方法:一个应用程序应该分解成GUI和后端两部分,它们之间只通过Apple Events通讯。GUI的工作是将低级的用户操作(键盘、鼠标)翻译成Apple Event并发送到后端。采取这种架构的程序天生就是有记录能力的,Apple Event管理器只要把GUI发往后端的事件全都记录下来就可以了。
AppleScript最让我感兴趣的地方是它那与自然语言极其类似的语法,但是在本文的最后,作者却认为这种语法搅乱了很多开发者的思维。我的思维被作者的这个结论也给搅乱了。
该文描述了Mac OS上的通用脚本语言AppleScript和通用的进程间通讯机制Apple Events。
支持脚本的(scriptable)应用程序中的数据对象包含(子)元素(elements)和属性(properties)字段。(子)元素字段可以有多个值,这些值都是(子)对象的引用,而属性字段的值要么是一个基本类型(如字符串、整数等)要么是一个对象引用。比如一个document包含若干个paragraph子元素,且有一个name属性, 一个paragraph有style和size属性,且包含word和character子元素。
Apple Events是一个标准远程过程调用(remote procedure calls)机制的变种,应用程序可以通过发送Apple Events来互相通讯。每个event都包含一个动词(verb)和若干参数,参数可以是基本类型值或者是一个object specifier(对象描述符)。对象描述符其实是一个查询表达式,远端程序对它解释求值后得到该程序内的数据对象(集合)。因此这种机制避免了远程对象虚体(stub)的存在。比如,下面的表达式求值后就得到一个textStyle属性集合:
the textStyle of character 1 to 10 of card field "Description"
一个对象描述符一般由properties(如textStyle)和elements(如character,card field)组成,elements后头可跟名字、下标或范围以从集合中选择element(s)。
AppleScript常用于复杂任务的自动化和应用程序的定制,它提供了常用的编程设施(control flow, variables, data structures)。它的一个主要功能是发送和接收Apple Events,发送一个Apple Event类似于一个过程调用,而接收到的Apple Event的处理则是由同名的subroutine负责。AppleScript提供了被称为
object references(对象引用)的特殊语法形式来处理Apple Event的object specifiers, 它的形式定义如下:
reference ::=
| property
| `beginning'
| `end'
| `before' term
| `after' term
| `some' singularClass
| `first' singularClass
| `last' singularClass
| term (`st' | `nd' | `rd' | `th') anyClass
| `middle' anyClass
| ( pluralClass | `every' anyClass) [`from' term toOrThrough term]
| anyClass term [toOrThrough term]
| singularClass `before' term
| singularClass `after' term
| term (`of' | `in' | `'s') term
| term (`whose' | `where' | `that') term
toOrThrough ::= `to' | `thru' | `through'
call ::= message `(' expr* `)'
| message [`in' | `of'] [term] arguments
| term name arguments
message ::= name | terminologyMessage
arguments ::= ( preposition expression | flag | record)*
flag ::= (`with' | `without') [name]+
record ::= `given' (name `:' expr)*
preposition ::=
`to' | `from' | `thru' | `through'
| `by' | `on' | `into' | terminologyPreposition
在AppleScript中对象引用的值就对应于一个对象描述符。如果有个操作是针对对象引用进行的,那么相应地就会有一个包含了正确的动词(verb)和对象描述符的Apple Event创建出来。下面是几个对象引用的例子:
the first word of paragraph 22
name of every figure of document "taxes"
the modification date of every file whose size > 1024
请注意最后一个例子, 它是一个带有条件测试的对象引用。Object references很象first-class的指针类型,可以(自动)解引用,也可以通过它更改相关的值。
每个支持脚本的(scriptable)应用程序会有一个对应的应用程序术语集(application terminology),里面定义了它支持的所有event,属性和元素的名字。而AppleScript本身也有自己的术语:
application "name" 标识一个应用程序;
application "appName" of machine "machineName" 标识一个在特定机器上的应用程序;
一段脚本命令可以通过tell发给特定的应用程序:
tell application "Excel" on machine x
put 3.14 into cell 1 of row 2 of window 1
end
因为Microsoft Excel的表格术语集(spreadsheet terms)包含有诸如cell、formula这样的名词和recalculate
这样的动词,所以在语句tell application "Excel" 引入的作用域内,脚本是可以使用所有Excel术语的(这与dynamically scoping很类似),就好象它们本来就是AppleScript的固有组成部分一样,这相当于对AppleScript的基本编程概念做了针对该应用领域的(domain-specific)扩展。
AppleScript的解析器必须能够处理语言内建的设施以及应用程序的术语集。术语集可以包含由多个单词(words)组成的名字,这意味这词法分析器必须能够识别由多个单词组成的一个完整的逻辑标识符。因此,词法分析器依赖语法解析器的状态:进入一个tell作用域时,token表被扩展,离开时被重置。
AppleScript有一个简单的对象模型。一个script对象包含有属性(properties)和方法(methods)。方法是动态派发的(dynamically dispatched),所以支持了简单的面向对象编程。下面是个例子:
script Counter
property count : 0
to increment
set count to count + 1
return count
end increment
end script
方法的定义语法有点象LOGO。上面脚本执行时会将一个新的script object绑定到名字Counter上。方法的定义里还可包含script定义以创建多个实例,这样的方法就是工厂方法(factory method)。 因为script能访问的变量是词法定界(lexically scoping)的,所以一个工厂方法创建的所有对象都能访问该工厂方法所属对象的内部状态,这种机制很象Smalltalk的class/metaclass机制。AppleScript的对象模型是类似于Self那样基于原型(prototype)的,而且通过将未处理的命令委托给该对象的parent属性值来支持单继承。
一些应用程序可以通过记录用户对GUI的操作来生成相应的AppleScript程序,比如,如果用户选择File Open菜单,然后在"Personal"目录下选择一个"Resume"文件,那么相应的Apple Event将是一个path参数为"Personal:Resume"的FileOpen事件。很显然,一个Apple Event往往由多个底层事件(鼠标点击等)组成,这种记录与那些只记录鼠标、键盘等低层消息的行为比显得更高级、更抽象,而且因为每个用户操作都要能用Apple Event来描述,所以也使得记录程序复杂了许多。为此,文章提出了一种架构方法:一个应用程序应该分解成GUI和后端两部分,它们之间只通过Apple Events通讯。GUI的工作是将低级的用户操作(键盘、鼠标)翻译成Apple Event并发送到后端。采取这种架构的程序天生就是有记录能力的,Apple Event管理器只要把GUI发往后端的事件全都记录下来就可以了。
AppleScript最让我感兴趣的地方是它那与自然语言极其类似的语法,但是在本文的最后,作者却认为这种语法搅乱了很多开发者的思维。我的思维被作者的这个结论也给搅乱了。