美文网首页
第三章 语法基础

第三章 语法基础

作者: _火魂_ | 来源:发表于2016-05-11 16:54 被阅读0次

    正如大多数现代Smalltalk方言,Squeak使用一种非常接近Smalltalk-80的语法。这个语法设计的非常符合英语阅读习惯

    (Smalltalk includes:Class) ifTrue:[Transcript show: Class superclass]
    

    Squeak语法核心非常简洁,仅仅包含方法声明与消息发送语法。表达式基于少量的原语元素构建。仅仅保护6个关键词,没有任何控制结构语法和声明新类语法。取而代之的是,逻辑控制ifelse等语言结构使用发送消息ifTrue到布尔对象,创建新的子类发送superclass消息给父类。

    3.1 语法要素

    表达式保护以下的内建块结构:

    1. 六个保留关键词 pseudo-variables:self super nil true false thisContext
    2. 字面量常量表达式 数字 字符 字符串 符号表和数组
    3. 变量声明
    4. 变量赋值
    5. 块结构
    6. 消息发送

    我可以看看表3.1中的语法要素使用例子

    syntax

    本地变量 Local 小写

    startPoint是一个变量名称或标识符。通常,标识符使用驼峰大小写camelCase。实例变量的首字符是小写的,表明这个变量是私有作用域

    共享变量 Shared 大写

    Transcript是一个全局变量,类TranscriptStream的实例。通常以大写字母开头。

    消息接受者 Receiver

    self是一个关键词代表当前方法所在的对象。我们将这个称为消息接受者,因为通常用来接收消息来执行方法。self称为预定义变量不可以手动赋值

    数字类型 Integers

    squeak除了十进制42,还提供了其他类型的数字,2r101等

    浮点数
    squeak提供了十进制的浮点数 2.4e7

    字符
    字符使用$进行声明 $a表示a。可以发生消息给字符Character类 来处理 如 space tab等

    字符串
    单引号用来定义字符串字面量,可以在单引号中使用双号'G"day'

    符号
    与字符串类似,符号包含一系列的字符,然而不同于字符串,符号字面量是全局唯一的。#hello是一个符号对象 'Hello'是一个字符串

    编译时数组

    使用#()定义编译时数组,包含空格分隔的字面量。其中的参数必须是编译时常量 #(27 #(true false) abc)是一个三个元素的编译时字面量数组

    运行时数组

    使用{}定义运行时数组,其中的元素使用句号分隔 {1. 2. 1+2} 顶一个包含三个元素的运行时数组,

    注释

    使用双引号包围字符串作为注释"hello"。在smalltalk中双引号不是字符串只是注释说明

    本地定义变量

    使用||包含方法中的一个或多个本地变量定义

    赋值

    使用:=赋值对象变量。有时可以使用<-代替。因此x := 1 等价于x <- 1x _ 1

    块语法

    使用[]表明块语法。作为块语法或谓词语法。通常代表一个一个方法对象。块语法可以接受形参和本地变量

    原语
    使用<primitive:...>引入一个虚拟机原语,其中的原语代码将会在原语执行失败时运行。

    一元消息
    仅仅一个方法名称的消息
    二元消息
    包含一个参数的方法的消息
    关键字消息
    包含多个参数的方法的小

    方法返回
    可以使用^ 返回方法的运算结果
    语句结束
    可以使用.语法作为语法分隔符。
    级联消息
    可以发送多个消息到一个对象,使用(;)实现级联消息发送

    3.2 预定义变量

    在Smalltalk中,包含6个保留关键词或者说是预定义变量
    nil,true,false,self,super,thisContext

    这些预定义变量的值是运行环境决定的,无法进行手动赋值,
    其中true,false,nil是常量值。
    self,super,thisContext是动态变量值

    truefalse是Boolean类的子类True和False的唯一实例对象。 在第八章详细讲解

    nil代表未定义对象。是UndefinedObject类的唯一实例对象。实例变量通常初始化为nil

    self 通常表示当前方法所在的对象,也就是方法消息的接受者对象。

    super当前方法所在对象的父级,发送消息给super,方法查找将会从当前对象类的父类开始进行查找。这个在第5章详解

    thisContext代表运行栈的顶帧。也就是说,代表当前运行方法上下文,块上下文。在大多数开发过程中不需要关心当前上下文,但是在实现调试器等开发工具中非常有用,也会用来实现异常处理和继续运行。

    3.3 消息发送

    在Squeak中包含三类消息

    1. 一元消息 (Unary message)
      通常不需要参数,如1 factorial发送消息factorial给数字对象 1

    2 .二元消息(Binary message)
    通常需要一个参数,如1 + 2发送消息+携带一个参数2给数字对象 1

    1. 关键字消息(Keyword message)
      通常可以携带任意数量的参数, 如2 raisedTo: 6 modulo: 10 发送消息包含多个消息选择器raisedTo: modulo:分别包含参数6 10到数字对象 2

    一元消息选择器通常使用字符数字字符,通常使用小写字母开头

    二元消息选择器包含以下的一个或多个字符
    +-/\*~<>=@%|&?,

    关键词消息选择包含一系列的关键词,每个关键词都使用小写字母开头并以分号结束

    一元消息选择器通常有最高的优先级,然后是二元消息选择器,最后是关键词消息选择器
    2 raisedTo: 1 + 3 factorial

    我们首先发送 factorial到数字对象3,然后发送6到数字对象1,最后我们发送raisedTo: 7到数字对象2.
    我们可以使用表达式—>表示这个表达式的计算结果

    如果不考虑优先级,我们通常直接计算如下
    1 + 2 * 3 —> 9
    因为没有优先级,所以先计算得到3然后相乘得到9
    然而我们包含的优先级法则如下
    1 + (2 * 3) —> 7

    消息发送可以使用句号和分号进行组合。

    一个句号分隔符可以将一系列表达式的依次一个接一个的运行。

    Transcript cr.
    Transcript show: 'hello world'.
    Transcript cr
    

    上面的语句将会发送cr,show:'hello world'到Transcript。最后再次发送cr。

    当多个消息被发送到相同的接受者,这些消息将会按照级联消息运行。接受者只需要被指明一次,一系列的消息按照级联的方式发送给消息接受者

    Transcript cr;
      show: 'hello world';
      cr
    

    上面两个表达式的执行结果将会相同

    3.4 消息语法

    在Squeak中,一个表达式可以在workspace,debugger,browser等任意的地方运行。

    方法的定义可以在系统查看器或者调试器中

    编程者通常在给的类的上下文中开发一个方法,一个类的定义通过发送消息给已存在的类,要求创建一个子类,因此不需要使用特定的语法来定义新的类

    下面是字符串类String中定义lineCount的方法。

    lineCount

    通常一个方法的定义包含以下结构

    1. 方法参数,包含方法名称和任意多个参数(lineCount)
    2. 方法注释,方法体中双引号中的字符串形描述
    3. 本地变量,使用||声明的本地变量
      4.方法体表达式,使用句号分隔的方法体表达式。

    可以使用^终止方法运行,返回运行结果。
    一个方法没有包含显式返回值的将默认返回self,
    这个默认实现链式运算

    参数和本地变量通常以小写字母开头,大写字母开头的变量假设为全局变量。例如Character类,仅仅是简单的全局变量代表字符类对象。

    3.5 块语法

    块语法提供一种机制来定义表达式的运算。一个块语法通常是匿名函数。可以给块语法发送value求值一个块语法。块语法可以使用^显示表明返回值,默认的通常返回一系列表达式最后一个表达式的值,

    [1 + 2] value —> 3
    

    块语法作为匿名函数,还可以接受形参,通常使用分号开头作为形参声明。使用垂直线分割形参与块语法

    [:x | 1 + x] value: 2 —> 3
    [:x :y | x + y] value: 1 value: 2 —> 3
    

    如果块语法包含多余四个参数的,必须使用valueWithArguments:,并且以数组形式传递参数。
    包含多个参数的块语法通常拾遗设计错误的标志

    块语法中可以声明本地变量,使用双竖线声明本地变量,通常本地变量在参数之后声明

    [:x :y | |z| z := x+y. z] value: 1 value: 2 —> 3
    

    块语法事实上词法作用域,因为我们可以将当前块的包围环境作为变量的作用域。下面的块语法限制了x变量作用范围

    |x|
    x := 1
    [:y | x + y] value:2 —> 3
    

    块语法通常是类BlockContext类的实例对象。也就是说它们仅仅是对象,可以被赋值给变量或者作为参数传递给任何其他对象

    3.6 条件与循环控制

    smalltal并没有提供任何特殊的语法用来实现控制结构。相反,这些仅仅典型的通过发送以语法块作为参数发送消息给布尔值,数字或者集合,

    条件语句通常发送ifTure:,ifFalse: ifTrue:ifFalse:给布尔值变量。

    (17*13>220)
      ifTrue:['bigger']
      ifFalse:['smaller']  —>'bigger'
    

    循环语句通常典型的发送消息给块,数字或者集合。因为循环语句的跳出条件或许会被反复运行,因此条件语句组织为一个可执行的块语法而不是一个布尔变量值,下面是一个典型的循环

    n := 1.
    [n < 1000] whileTrue: [ n := n*2].
    n —> 1024
    

    whileFlase: 用来退出循环

    n := 1.
    [n > 1000] whileFalse: [n := n*2].
    n —> 1024
    

    timeRepest: 实现固定次数循环

    n := 1.
    10 timesRepeat: [n := n*2].
    n —> 1024
    

    还可以使用to:do发送给一个数字,这个数字作为循环计数的初始化值。第二个参数作为上限,语法块接受当前计数值,运行求值结果

    n := 0 .
    1 to: 10 do: [:counter | n:= n + counter ] .
    n —> 55
    

    高级迭代器。集合中包含大量的不同的类,这些类通常支持相同的协议。可以用于集合迭代的消息类有do:,collect:,select:,reject:,detect: inject:into:。这些消息定义了较为高级的迭代器。使用这些迭代器可以简化代码

    Interval可以作为一系列数字集合,通常包含开始与结束位置信息。1 to: 10代表从1到10的数字集合。因为这个是数字集合,我们可以发送do:给数字集合,携带一个块语法参数可以用来遍历执行

    n := 0.
    (1 to: 10) do: [:element|n := n +element ].
    n —>  55
    

    可以使用 collect:构建一个集合,转换元素集合

    (1 to: 10) collect:[:each|each * each]
    —> #(1 4 9 16 25 36 49 64 81 100)
    

    可以使用select:reject:构建新的集合,每个包含(不)符合一定条件的选择内容。detect:返回固定集合的第一个元素

    'hello there' select: [:char | char isVowel] —>  'eeee'
    'hello there' reject:  [:char | char isVowel] —> 'hll thr'
    'hello there' detect: [:char | char isVowel] —> $e
    

    最终,有些集合在inject:into:方法中支持fold运算符。
    通常我们使用一个种子值然后使用一个表达式累积起来得到结果,通常使用求和和求积

    (1 to: 10) inject: 0 into:[:sum :each |sum + each]
    —> 55
    

    等价于使用
    0+1+2+3+4+5+6+7+8+9+10

    更多的有关集合与流的操作可以在第九章与第十章中找到

    3.7 原语与程序组织

    在smalltalk中,每个变量都是对象,每个变量都可以发送消息。然而,特定的时刻我们可能接触到岩底。特定的对象仅仅通过调用虚拟机的原语才可以得到执行

    比如,下面的操作通常实现为原语结构:内存操作(new new:),二进制运算(bitAnd:,bitOr:,bitShift),指针操作和数字运算(+ - < > * / = ==)还有数组访问(at:,at:put:)

    原子操作可以调用原子语法实现<primirive:aNumber>。调用原语操作的过程可能包含Smalltalk代码,通常是在原语执行失败的时候运行。

    可以查看SmallInteger>>+代码。如果原语执行失败,表达式 + aNumber将会得到执行并返回

    + aNumber
      <privmitive: 1>
    ^ super + aNumber
    

    Squeak3.9后,尖括号语法也会用在方法作为编译指示的注释使用。

    相关文章

      网友评论

          本文标题:第三章 语法基础

          本文链接:https://www.haomeiwen.com/subject/iuxurttx.html