两周自制脚本语言[试读]
1.1 机器语言与汇编语言
——某大学研究室内 C 话说,我现在正在写一本新书。 H 老师,您这次写的是什么主题的书呢? C 是一本和编译相关的书。确切地说,是关于语言处理器的书。 F 这样啊,这次是要写成一本教科书吗? C 不,出版社要求我这次写得通俗些,所以这本书的内容会比教材来得简单。 H 那这次还会像前一本书 那样,通过对话形式进行解说吗? C 这个问题现在还没有确定。有人赞成用对话的形式,但也有人反对。 F 老师,那这次的新书中会出现哪些人物呢?肯定会有H吧,毕竟这里他最年长。 H 哎呀,别这么说,就算没有我也没关系。 F H肯定会出现啦。至于还会有哪些人,真是很期待呀。此外,M 那样称... 查看全部[ 1.1 机器语言与汇编语言 ]
1.2 解释器与编译器
语言处理器可大致分为解释器与编译器两种。这两类语言处理器的执行原理有很大差异。 解释器 解释器根据程序中的算法执行运算。简单来讲,它是一种用于执行程序的软件。如果执行的程序由虚拟机器语言或类似于机器语言的程序设计语言写成,这种软件也能称为虚拟机。 编译器 编译器能将某种语言写成的程序转换为另一种语言的程序。通常它会将原程序转换为机器语言程序。编译器转换程序的行为称为编译,转换前的程序称为源代码或源程序。如果编译器没有把源代码直接转换为机器语言,一般称为源代码转换器或源码转换器(source code translator)。 程序设计语言提供了何种类型的语言处理器不一而论... 查看全部[ 1.2 解释器与编译器 ]
1.3 开发语言处理器
本书将为极为简单的脚本语言开发语言处理器。由于对象是脚本语言,所以如果按上一节的分类方式,本书开发的语言处理器属于解释器。不过,该解释器内部将采用编译器来提高性能,因此本书也将涉及开发编译器的一些基本知识。本书不包含代码优化之类的技巧,因此不会介绍诸如编译器在将程序转换为机器语言时,如何提高机器语言的执行效率等内容。 F 脚本语言这个词的含义有些模糊不是吗? C 嗯,这的确是一个无法回避的问题。 H 要回答脚本语言是怎样的程序设计语言,实在是不容易。 C 总之,我们并不是要设计C语言那样的语言。不过,这类主题的书常会选择C语言的某些简化版本作为研究对象呢。 F 本书会包含通过正则... 查看全部[ 1.3 开发语言处理器 ]
1.4 语言处理器的结构与本书的框架
无论是解释器还是编译器,语言处理器前半部分的程序结构都大同小异。如图1.1所示,源代码首先将进行词法分析,由一长串字符串细分为多个更小的字符串单元。分割后的字符串称为单词。之后处理器将执行语法分析处理,把单词的排列转换为抽象语法树。至此为止,解释器与编译器的处理方式相同。之后,编译器将会把抽象语法树转换为其他语言,而解释器将会一边分析抽象语法树一边执行运算。 F 首先需要把源代码转换为抽象语法树没错吧? C 程序的分析结果能由抽象语法树表现,因此无论是解释器还是编译器都需要用到抽象语法树。 图1.1 语言处理器内部的处理流程 今后,本书将根据这一流程开发Stone语言的... 查看全部[ 1.4 语言处理器的结构与本书的框架 ]
2.1 麻雀虽小、五脏俱全的程序设计语言
从本章开始,我们将逐步实现一种名为Stone语言的程序设计语言。在具体实现之前,我们必须设计Stone语言的语法。本章将讨论如何设计Stone语言。如果想要从零开始设计一种新颖实用的语言,结果往往是半途而废。即使设计成功,也可能由于过于复杂难以实现等原因而最终不了了之。因此,本书将首先设计一种极为简单的语言,并开发相应的语言处理器,确保程序能够正确运行。之后,再慢慢向其中添加诸如面向对象等一些复杂的语言功能。也就是说,先设计出一个简化的成品,再逐步改良。 2.1 麻雀虽小、五脏俱全的程序设计语言 一种程序设计语言至少需要具备哪些语法功能呢?整数四则运算之类的功能自然必不可少,最好还能支持字... 查看全部[ 2.1 麻雀虽小、五脏俱全的程序设计语言 ]
2.2 句尾的分号
Stone语言为了简化语法,省去了if语句及while语句的条件表达式两侧的括号,并允许用户省略可以省略的句尾分号。如果同一行中写有多句语句,各句句尾的分号则不能省略。此时,分号用于区分不同的语句。 此外,{}括起来的代码块中最后一条语句的句尾分号能够省略。也就是说,如果句尾直接跟着},就不必使用分号。 { x = 1; y = 2 } 在上例中,y = 2之后没有分号。分号并不是一句语句结束的标识,而是代码块中语句之间的分隔符。因此,下面的代码块中含有3条语句,而不是2条: { x = 1; y = 2; } 其中第三条应该被视为一条空语句。空语句指的是没有内容的语... 查看全部[ 2.2 句尾的分号 ]
2.3 含糊不得的语言
如果代码中能够随处省略分号,并可以任意换行,似乎可读性就会提升。但要是一种程序设计语言的各种语法元素都能省略,语句中任何地方都能换行,它就可能会变得模棱两可,引起误解。 如果语言的含义不清,程序员就无法判断程序的实际执行方式,这会造成很大的麻烦。事实上,如何为这种语言设计语言处理器也很让人头疼。在设计一种语言时,设计者必须多加注意,确保语言中不出现模棱两可的歧义语法。 A 刚刚讨论的句尾分号的省略问题,不会有什么歧义吧? H 如果一个分号不能省略,又没有明确的不可省略的理由,会让人感到很困惑呢。 C 我在设计时已经尽力避免出现这种情况了。不过刚才的说明确实不够明了,不太容易理解。 ... 查看全部[ 2.3 含糊不得的语言 ]
3.1 Token对象
语言处理器的第一个组成部分是词法分析器(lexical analyzer、lexer或scanner)。程序的源代码最初只是一长串字符串。从内部来看,源代码中的换行也能用专门的(不可见)换行符表示,因此整个源代码是一种相连的长字符串。这样的长字符串很难处理,语言处理器通常会首先将字符串中的字符以单词为单位分组,切割成多个子字符串。这就是词法分析。 3.1 Token对象 程序设计领域中的单词包含+或==之类的符号。例如,下面是某个程序中的一行代码。 while i < 10 { 词法分析会把它拆分为下面这样的字符串。 "while" &quo... 查看全部[ 3.1 Token对象 ]
3.2 通过正则表达式定义单词
要设计词法分析器,首先要考虑每一种类型的单词的定义,规定怎样的字符串才能构成一个单词。这里最重要的是不能有歧义。某个特定的字符串只能是某种特定类型的单词。举例来讲,要是字符串123h既能被解释为标识符,又能被解释为整型字面量,之后的处理就会相当麻烦。这种单词的定义方式是不可取的。 代码清单3.1 Token.java package stone; public abstract class Token { public static final Token EOF = new Token(-1){}; // end of file public static... 查看全部[ 3.2 通过正则表达式定义单词 ]
3.3 借助java.util.regex设计词法分析器
只要能够通过正则表达式来表示单词的定义,词法分析器的设计就没有太大的困难。Java语言的正则表达式库能够在模式匹配后返回匹配的字符串中的一部分,本书将利用这一功能来实现词法分析器。例如,下面的字符串 http://javassist.org/ 与正则表达式 http://(.+)/ 匹配。Java语言能够获取与括号中的模式.+匹配的子字符串javassist.org。如果模式包含多个括号,各个括号内的子字符串都能被分别取得。 每一对左右括号都对应了与其包围的模式相匹配的子字符串。要利用这一功能设计词法分析器,首先要准备一个下面这样的正则表达式。 s*((//.*... 查看全部[ 3.3 借助java.util.regex设计词法分析器 ]
3.4 词法分析器试运行
本章的最后将尝试运行由代码清单3.3的Lexer类实现的词法分析器。相应的main方法如代码清单3.5所示。它将对输入的字符串做词法分析,并逐行显示分析得到的每一个单词(图3.1)。 代码清单3.6中的CodeDialog对象是Lexer类的构造函数中的参数。CodeDialog是java.io.Reader的子类。Lexer在调用read方法从该对象中读取字符时,界面上将显示一个对话框,用户输入的文本将成为read方法的返回值。如果上一次显示对话框时输入的文本没有被删除,这些文本将首先被返回。用户点击对话框的取消按钮后,输入结束。 图3.1 执行LexerRunner 代码清... 查看全部[ 3.4 词法分析器试运行 ]