Javac (J2SE) 编译器的词法分析介绍

Javac (J2SE) 编译器的词法分析介绍
Javac (J2SE) 编译器的词法分析介绍

第二章词法分析 (1)

2.1 单词符号的定义 (3)

2.2 词法分析程序的基本数据结构 (4)

2.3 词法分析程序的初始化 (7)

2.4 扫描下一个字符 (8)

2.5 扫描下一个符号 (9)

2.6 滤除源程序中注释 (14)

2.7 读取一个标识符 (15)

2.8 读取一个数值常量 (16)

2.9 词法分析程序运行实例 (17)

3.0 词法分析程序小结 (19)

第二章词法分析

将高级语言翻译成较低级的、面向机器的汇编语言或者某种中间表示,是一件非常复杂和困难的事情。自从IBM的计算机科学家第一次实现Fortran语言的编译器以来,全世界的计算机科学家对编译的技术进行了长期的研究和探索,总结出了一套行之有效的、对一般的高级程序设计语言普遍适用的翻译方法。这套方法的主要思想就是,把高级语言的翻译这个大问题划分为若干个相对容易解决的小问题,然后采用分而治之的策略逐个给出解决的方法。该划分一般是把编译器的主要任务分为:词法分析,语法分析和语义分析。其中词法分析要解决的问题是:从输入文件中读取字符形式的高级语言源程序,并把输入转化为一个由单词符号组成的流供语法分析部分进一步分析。这样使语法分析部分不必关心,例如单词符号如何组成,这样的细节问题,从而简化了语法分析的设计。

词法分析程序要完成的工作有:

?过滤掉源程序中空白字符和注释,因为这些信息仅仅增加了源程序的可读性便于程序员阅读和维护程序,而对于语法分析是完全无用的。

?识别各种常量,并且把字符形式的表示翻译成编译器的内部表示。

?识别标识符和关键字

?识别源程序中的各种符号

程序设计语言的单词符号可以用乔姆斯基3型文法,也即正则文法描述。所谓正则文法,其产生式集合中仅含有如下形式的产生式:

α→βα∈Vn, β=a 或者β=aB

其中a∈Vt, B∈Vn

由这种文法所产生的语言被认为是正则的。由形式语言和自动机理论可知,正则文法与正则表达式是等价的,并且正则文法所描述的语言可以用一种有穷自动机来识别。

下面是正则表达式的递归定义:

(1)Φ是一个表示空集的正则表达式

(2)ε是一个正则表达式,它所表示的语言仅包含一个空符号串,即{ε}

(3)a是一个正则表达式,a∈Vt。它所表示的语言是由单个符号所组成的,即{a}

(4)如果e1和e2是正则表达式,其表示的语言分别为L1和L2,则:

(e1)|(e2)是一个表示语言L1∪L2的正则表达式

(e1)(e2)是一个表示语言L1L2的正则表达式

{e1}是一个表示语言L1*的正则表达式

确定性有穷自动机,简称作DFA,它的定义如下。

一个确定性有穷自动机M是一个五元式:

M=(Q,Vt,δ,q0,F)

其中:

Q:有穷状态集合

Vt:输入符号表

δ:状态转换函数,为QⅹVt→Q的映射

q0:初始状态,q0∈Q

F:终止状态集,F?Q

非确定性有穷自动机,简称NFA,它的定义如下。

一个非确定性有穷自动机M’是一个五元式:

M’=(Q,Vt∪{ε},δ,q0,F)

其中:

Q:有穷状态集合

Vt∪{ε}:输入符号表与空符号串组成的集合

δ:状态转换函数,为QⅹVt∪{ε}→2Q的映射,2Q表示Q的幂集

q0:初始状态,q0∈Q

F:终止状态集,F?Q

词法分析程序一般来说比较简单,很容易手工编程实现。另一种方法就是利用lex等自动生成工具来生成。(lex生成的是用C语言来实现的词法分析程序,要生成java语言实现的词法分析程序,可以使用javaCC,它是语法分析程序和词法分析程序的自动生成工具。)使用自动生成工具以后,编译器的构造过程变成了如下形式:(以lex为例)

图2.1 编译器的构造过程

词法分析程序的自动生成器一般采用如下原理工作。它接受以正则表达式为输入形式的对程序设计语言所使用的单词符号的描述。构造一个能够识别这个正则表达式所描述的正则语言的确定性有穷自动机(DFA)。在这个构造过程中,一般来说要借助于非确定性有穷自动机(NFA),也就是说,把正则表达式的描述首先转化为与其等价的非确定性有穷自动机,然后再转化为与其等价的确定性有穷自动机。最后生成(输出)的词法分析程序,就是这个确定性有穷自动机的实现,它能够识别程序设计语言中的各种单词符号。

一般来说,开发一种新语言时,由于它的单词符号在不停地修改,采用lex等工具生成的词法分析程序比较易于修改和维护。一旦一种语言确定了,则采用手工编写词法分析程序效率更高。在gcj中,采用了手工的方式而没有借助任何工具,使用java语言实现了一个java 语言的词法分析程序。

2.1 单词符号的定义

一般来说,不论什么语言实现编译器,都要使用整型的常量来定义各种单词符号。例如在C语言中可以用如下的形式来对单词符号来进行定义:

#define NUMBER 0

#define IDENTIFIER 1

……

下面看一下在GJC中是如何使用java语言来定义各种单词符号的。下面的程序片断出自parser/Tokens.java

1 /**

2 * @(#)Tokens.java 1.11 03/01/23

3 *

4 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.

5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

6 */

7 package com.sun.tools.javac.v8.parser;

8

9 /**

10 * An interface that defines codes for Java source tokens

11 * returned from lexical analysis.

12 */

13 public interface Tokens {

14 public static final int EOF = 0;

15 public static final int ERROR = EOF + 1;

16 public static final int IDENTIFIER = ERROR + 1;

17 public static final int ABSTRACT = IDENTIFIER + 1;

18 public static final int ASSERT = ABSTRACT + 1;

19 public static final int BOOLEAN = ASSERT + 1;

20 public static final int BREAK = BOOLEAN + 1;

21 public static final int BYTE = BREAK + 1;

。。。。。。

119 public static final int GTGTEQ = LTLTEQ + 1;

120 public static final int GTGTGTEQ = GTGTEQ + 1;

121 public static final int TokenCount = GTGTGTEQ + 1;

122 }

需要注意在GJC中,对于java语言中的每一个关键字都有一个常量与其对应(例如EOF = 0),而不是采用一个统一的单词符号,例如KEYWORD,来标记它们。

2.2 词法分析程序的基本数据结构

词法分析程序一般在英语中被称为lexer或者scanner,在GJC中,parser/Scanner.java 就是它的词法分析程序的实现。在Scanner这个类中,有几个关键的成员需要注意:在第22行声明了token,它表明了当前的单词符号是什么:标识符?java语言中的某一个关键字ABSTRACT,BOOLEAN?或者是其它在Tokens.java中定义的java符号?

在第48行声明了name。当词法分析程序识别出当前处理的单词符号是标识符时,仅仅告诉语法分析程序这是一个标识符是不够的,还要提供该标识符的具体名字,以便将它填入到符号表中。这里声明name的目的就是完成这个功能。第103行定义的names就是存放这些名字的字符串表示的名字表(name table)。

在第78行中声明了ch,这是当前处理的字符。后面还定义了一些与该字符相关联的信息,该字符所在的行line和列col,这些内容在出错处理时用来提供和源程序位置相关的信息。

1 /**

2 * @(#)Scanner.java 1.36 03/01/23

3 *

4 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.

5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

6 */

7 package com.sun.tools.javac.v8.parser;

8 import java.io.*;

9

10 import com.sun.tools.javac.v8.util.*;

11

12

13 /**

14 * The lexical analyzer maps an input stream consisting of

15 * ASCII characters and Unicode escapes into a token sequence.

16 */

17 public class Scanner implements Tokens, LayoutCharacters {

18

19 /**

20 * The token, set by nextToken().

21 */

22 int token;

23

24 /**

25 * The token's position. pos = line << Position.LINESHIFT + col.

26 * Line and column numbers start at 1.

27 */

28 int pos;

29

30 /**

31 * The last character position of the token.

32 */

33 int endPos;

34

35 /**

36 * The last character position of the previous token.

37 */

38 int prevEndPos;

39

40 /**

41 * The position where a lexical error occurred;

42 */

43 int errPos = Position.NOPOS;

44

45 /**

46 * The name of an identifier or token:

47 */

48 Name name;

49

50 /**

51 * The radix of a numeric literal token.

52 */

53 int radix;

54

55 /**

56 * Has a @deprecated been encountered in last doc comment?

57 * this needs to be reset by client.

58 */

59 boolean deprecatedFlag = false;

60

61 /**

62 * A character buffer for literals.

63 */

64 private char[] sbuf = new char[128];

65 private int sp;

66

67 /**

68 * The input buffer, index of next chacter to be read,

69 * index of one past last character in buffer.

70 */

71 private char[] buf;

72 private int bp;

73 private int buflen;

74

75 /**

76 * The current character.

77 */

78 private char ch;

79

80 /**

81 * The line number position of the current character.

82 */

83 private int line;

84

85 /**

86 * The column number position of the current character.

87 */

88 private int col;

89

90 /**

91 * The buffer index of the last converted unicode character

92 */

93 private int unicodeConversionBp = 0;

94

95 /**

96 * The log to be used for error reporting.

97 */

98 private final Log log;

99

100 /**

101 * The name table.

102 */

103 private final Name.Table names;

104

105 /**

106 * The keyword table.

107 */

108 private final Keywords keywords;

109

110 /**

111 * Documentation string of the current token.

112 */

113 String docComment = null;

114

115 /**

116 * Buffer for doc comment.

117 */

118 private char[] buffer;

119

120 /**

121 * Number of characters in doc comment buffer.

122 */

123 private int count;

124

125 /**

126 * Construct a scanner from a file input stream, a log, and a

127 * character encoding.

128 */

2.3 词法分析程序的初始化

如下面程序片断所示,GJC词法分析程序的初始化在Scanner的构造函数中完成,主要完成了把输入流中的字符读到内存的缓冲区中,预置当前字符的位置,并且调用nextToken()方法预读一个符号(token)。

129 public Scanner(Context context, InputStream in, String encoding) {

130 super();

131 this.log = Log.instance(context);

132 https://www.360docs.net/doc/6718220903.html,s = Name.Table.instance(context);

133 this.keywords = Keywords.instance(context);

134 try {

135 int bufsize = in.available() + 1;

136 if (buf == null || buf.length < bufsize)

137 buf = new char[bufsize];

138 buflen = 0;

139 InputStreamReader reader =

140 (encoding == null) ? new InputStreamReader(in) :

141 new InputStreamReader(in, encoding);

142 while (true) {

143 int nread = reader.read(buf, buflen, buf.length - buflen); 144 if (nread < 0)

145 nread = 0;

146 buflen = buflen + nread;

147 if (buflen < buf.length)

148 break;

149 char[] newbuf = new char[buflen * 2];

150 System.arraycopy(buf, 0, newbuf, 0, buflen);

151 buf = newbuf;

152 }

153 } catch (UnsupportedEncodingException e) {

154 lexError("unsupported.encoding", encoding);

155 buf = new char[1];

156 buflen = 0;

157 }

158 catch (IOException e) {

159 lexError("io.exception", e.toString());

160 buf = new char[1];

161 buflen = 0;

162 }

163 buf[buflen] = EOI;

164 line = 1;

165 col = 0;

166 bp = -1;

167 scanChar();

168 nextToken();

169 }

170

2.4 扫描下一个字符

因为整个输入文件已经在词法分析程序初始化时读入到内存的缓冲区当中,所以读取一个字符的操作就很简单了,只需要移动字符指针找到下一个字符,并把它赋给词法分析程序中表示当前要处理字符的成员变量ch就可以了。见scannner.java,scanChar()方法中270-271行。

但是需要注意维护当前字符所在的位置信息。

在下面的实现中还需要注意:GJC对java的unicode 支持,由于这不属于编译理论的内容这里不详细展开。

265

266 /**

267 * Read next character.

268 */

269 private void scanChar() {

270 bp++;

271 ch = buf[bp];

272 switch (ch) {

273 case '\r':

274 col = 0;

275 line++;

276 break;

277

278 case '\n':

279 if (bp == 0 || buf[bp - 1] != '\r') {

280 col = 0;

281 line++;

282 }

283 break;

284

285 case '\t':

286 col = (col / TabInc * TabInc) + TabInc;

287 break;

288

289 case '\\':

290 col++;

291 convertUnicode();

292 break;

293

294 default:

295 col++;

296 break;

297

298 }

299 }

300

2.5 扫描下一个符号

扫描下一个符号,就是根据当前的字符ch来判断下面的符号可能是什么,然后调用相应的处理函数来处理。在GJC中,这是由nextToken()方法来完成的,该方法也是词法分析程序提供给语法分析程序的调用接口。

注意词法分析程序的一个重要任务——空白字符的过滤就是在这里完成,详见nextToken()方法的962-973行,简单的调用scanChar()方法,不做其它任何处理。

当前字符为26个英语字母中的一个或者是“$”和“_”时,当前处理的符号应该是一个标识符,调用scanIdent()方法对其余部分进行扫描,见nextToken()方法的975-1083行。

当前字符为“0”,并且其后紧跟着字母“x”,那么这应该是一个16进制常量,如果后面是其他数字则应该是一个8进制常量。如果当前字符为非0的数字,则应该是一个10进制常量。对于上述情况分别调用scanNumber()方法来处理。详见nextToken()方法1085-1118行。

当前字符为“.”,则应该分情况处理,如果其后紧跟数字则应该是一个小数,否则就是一个普通的单词符号DOT。详见nextToken()方法的1119-1128行。

对于“,”、“:”等单独的字符,由于它们不是多个单词符号公有的字符,所以它们直接对应着其所表示的单词符号。详见nextToken()方法的1129-1168行

对于“/”这个字符的处理最为复杂,因为如果它的后面紧跟着另一个“/”,则是一个到行尾的注释;如果它的后面紧跟着另一个“/*”,则是一个多行注释;如果它的后面紧跟着另一个“/**”,则是一个多行文档注释,可以用javadoc等工具自动提取出来生成HTML 格式的文档;如果它的后面紧跟着另一个“=”,则是一个除法赋值运算符;否则就是一个简单的除法运算符。注意滤掉源程序中注释的功能就是在这里实现的。详见nextToken()方

法的1169-1199行。

对于“’”、“””这两个字符,后面应该是字符常量和字符串常量,分别调用相应的处理函数进行处理。详见nextToken()方法的1200-1229行

950

951 /**

952 * Read token.

953 */

954 public void nextToken() {

955 try {

956 prevEndPos = endPos;

957 sp = 0;

958 docComment = null;

959 while (true) {

960 pos = (line << Position.LINESHIFT) + col;

961 int start = bp;

962 switch (ch) {

963 case ' ':

964

965 case '\t':

966

967 case FF:

968

969 case CR:

970

971 case LF:

972 scanChar();

973 break;

974

975 case 'A':

976

977 case 'B':

。。。。。。

1072

1073 case 'x':

1074

1075 case 'y':

1076

1077 case 'z':

1078

1079 case '$':

1080

1081 case '_':

1082 scanIdent();

1083 return;

1084

1085 case '0':

1086 scanChar();

1087 if (ch == 'x' || ch == 'X') {

1088 scanChar();

1089 if (digit(16) < 0) {

1090 lexError("invalid.hex.number"); 1091 }

1092 scanNumber(16);

1093 } else {

1094 putChar('0');

1095 scanNumber(8);

1096 }

1097 return;

1098

1099 case '1':

1100

1101 case '2':

1102

1103 case '3':

1104

1105 case '4':

1106

1107 case '5':

1108

1109 case '6':

1110

1111 case '7':

1112

1113 case '8':

1114

1115 case '9':

1116 scanNumber(10);

1117 return;

1118

1119 case '.':

1120 scanChar();

1121 if ('0' <= ch && ch <= '9') {

1122 putChar('.');

1123 scanFractionAndSuffix();

1124 } else {

1125 token = DOT;

1126 }

1127 return;

1128

1129 case ',':

1130 scanChar();

1131 token = COMMA;

1132 return;

1133

1134 case ';':

1135 scanChar();

1136 token = SEMI;

1137 return;

1138

1139 case '(':

1140 scanChar();

1141 token = LPAREN; 1142 return;

1143

1144 case ')':

1145 scanChar();

1146 token = RPAREN; 1147 return;

1148

1149 case '[':

1150 scanChar();

1151 token = LBRACKET; 1152 return;

1153

1154 case ']':

1155 scanChar();

1156 token = RBRACKET; 1157 return;

1158

1159 case '{':

1160 scanChar();

1161 token = LBRACE; 1162 return;

1163

1164 case '}':

1165 scanChar();

1166 token = RBRACE; 1167 return;

1168

1169 case '/':

1170 scanChar();

1171 if (ch == '/') {

1172 do {

1173 scanCommentChar();

1174 } while (ch != CR && ch != LF && bp < buflen)

1175 ;

1176 break;

1177 } else if (ch == '*') {

1178 scanChar();

1179 if (ch == '*') {

1180 docComment = scanDocComment();

1181 } else {

1182 skipComment();

1183 }

1184 if (ch == '/') {

1185 scanChar();

1186 break;

1187 } else {

1188 lexError("https://www.360docs.net/doc/6718220903.html,ment");

1189 return;

1190 }

1191 } else if (ch == '=') {

1192 name = names.slashequals;

1193 token = SLASHEQ;

1194 scanChar();

1195 } else {

1196 name = names.slash;

1197 token = SLASH;

1198 }

1199 return;

1200

1201 case '\'':

1202 scanChar();

1203 if (ch == '\'') {

1204 lexError("empty.char.lit");

1205 } else {

1206 if (ch == CR || ch == LF)

1207 lexError(pos, "illegal.line.end.in.char.lit"); 1208 scanLitChar();

1209 if (ch == '\'') {

1210 scanChar();

1211 token = CHARLITERAL;

1212 } else {

1213 lexError(pos, "unclosed.char.lit");

1214 }

1215 }

1216 return;

1217

1218 case '\"':

1219 scanChar();

1220 while (ch != '\"' && ch != CR && ch != LF && bp < buflen) 1221 scanLitChar();

1222 if (ch == '\"') {

1223 token = STRINGLITERAL;

1224 scanChar();

1225 } else {

1226 lexError(pos, "unclosed.str.lit");

1227 }

1228 return;

1229

1230 default:

1231 if (isSpecial(ch)) {

1232 scanOperator();

1233 } else if (Character.isJavaIdentifierStart(ch)) {

1234 scanIdent();

1235 } else if (bp == buflen || ch == EOI && bp + 1 == buflen) {

1236 token = EOF;

1237 } else {

1238 lexError("illegal.char", String.valueOf((int) ch)); 1239 scanChar();

1240 }

1241 return;

1242

1243 }

1244 }

1245 }

1246 finally { endPos = (line << Position.LINESHIFT) + col - 1;

1247 } }

1248 }

2.6 滤除源程序中注释

对于多行注释,需要不断的查找直到遇到“*”后面紧跟着“/”时,才表示该注释结束。详见paser/scanner.java中的skipComment()方法,760-780行。GJC中还有scanDocComment()方法,也使用类似的技术来处理源程序中的注释,由于篇幅限制这里就不展开介绍了。

760

761 /**

762 * Skip a non-documentation comment. This method should be called once 763 * the initial /, * and the next character have been read.

764 */

765 private void skipComment() {

766 while (bp < buflen) {

767 switch (ch) {

768 case '*':

769 scanChar();

770 if (ch == '/')

771 return;

772 break;

773

774 default:

775 scanCommentChar();

776 break;

777

778 }

779 }

780 }

2.7 读取一个标识符

GJC中标识符的处理方法是把字符不断的放入缓冲区sbuf中,直到遇到不能作为标识符中的字符为止。注意可以出现在标识符中的字符有字母,数字,还有“$”和“_”。当所有的字符都读入后,把缓冲区sbuf中的字符串,转化为一个Name对象,查关键字表,如果是关键字则返回相应的单词符号。

534 /**

535 * Read an identifier.

536 */

537 private void scanIdent() {

538 do {

539 if (sp == sbuf.length)

540 putChar(ch);

541 else

542 sbuf[sp++] = ch;

543 scanChar();

544 switch (ch) {

545 case 'A':

546

547 case 'B':

。。。。。。

642

643 case 'x':

644

645 case 'y':

646

647 case 'z':

648

649 case '$':

650

651 case '_':

652

653 case '0':

654

655 case '1':

。。。。。。

670

671 case '9':

672 break;

673

674 default:

675 if (!Character.isJavaIdentifierPart(ch) || bp >= buflen) { 676 name = names.fromChars(sbuf, 0, sp);

677 token = keywords.key(name);

678 return;

679 }

680

681 }

682 } while (true)

683 ;

684 }

2.8 读取一个数值常量

根据java语言的定义,下面的程序可以处理123,123.4,123e5,123L等类型的数值常量,并把组成这个常量的字符通过调用putChar()方法,填入到sbuf缓冲区中。详见scanNumber()方法504-533行。scanFractionAndSuffix()和scanNumber()方法类似,这里不再展开。

504

505 /**

506 * Read a number.

507 * @param radix The radix of the number; one of 8, 10, 16.

508 */

509 private void scanNumber(int radix) {

510 this.radix = radix;

511 int digitRadix = (radix <= 10) ? 10 : 16;

512 while (digit(digitRadix) >= 0) {

513 putChar(ch);

514 scanChar();

515 }

516 if (radix <= 10 && ch == '.') {

517 putChar(ch);

518 scanChar();

519 scanFractionAndSuffix();

520 } else if (radix <= 10 &&

521 (ch == 'e' || ch == 'E' || ch == 'f' || ch == 'F' || ch == 'd' ||

522 ch == 'D')) {

523 scanFractionAndSuffix();

524 } else {

525 if (ch == 'l' || ch == 'L') {

526 scanChar();

527 token = LONGLITERAL;

528 } else {

529 token = INTLITERAL;

530 }

531 }

532 }

533

注意,在GJC中没有使用这些数值常量的二进制值作为它们的内部表示,而是采用表示这些常量的字符串作为内部表示。通过stringVal()方法提供给上层程序关于值的信息。

943 /**

944 * The value of a literal token, recorded as a string.

945 * For integers, leading 0x and 'l' suffixes are suppressed.

946 */

947 public String stringVal() {

948 return new String(sbuf, 0, sp);

949 }

950

2.9 词法分析程序运行实例

词法分析程序完成从输入文件中读取字符形式的高级语言源程序,并把输入转化为一个由单词符号组成的流,供语法分析程序进一步分析。为了更直观的将这一过程展现给读者,我们在GJC中增加了将每个词法分析程序识别出的单词符号输出的功能。这样读者就可以更清楚地了解在编译器中词法分析程序所作的工作了。下面的程序中,dumpToken()方法就

实现了这个功能,我们把dumpToken()方法添加在Scanner.java文件的Scanner类中作为一个成员方法。

private void dumpToken(){

switch(token){

case EOF : System.out.print(" EOF ");break;

case ERROR : System.out.print(" ERROR ");break;

case IDENTIFIER : System.out.print(" IDENTIFIER ");break;

case ABSTRACT : System.out.print(" ABSTRACT ");break; 。。。。。。。。

。。。。。。。。

case LTLTEQ : System.out.print(" LTLTEQ ");break;

case GTGTEQ : System.out.print(" GTGTEQ ");break;

case GTGTGTEQ : System.out.print(" GTGTGTEQ ");break;

default : System.out.print(" UNKNOWNTOKEN ");break;

}

}

程序的功能和实现都很简单,我们只需要在每次调用nextToken()方法的时候,首先调用dumpToken()方法输出当前的单词符号。

下面是一个小的Java程序,作为GJC的输入,我们可以通过它来观察词法分析程序所完成的功能。该Java程序如下:

1 /**

2 * Compiler Test

3 * Copyright 200

4 BeiHang University. All Rights Reserved

4 * @author anonymous

5 */

6

7 public class TestCompiler {

8 public TestCompiler(){

9 Name=new String("Hello World!");

10 }

11 public int testAdd(int a, int b){

12 int result;

13 result=a+b;

14 return result;

15 }

16 public void testLoop(){

17 int sum=0;

18 for (int i=1;i<=100;i++)

19 sum+=i;

20 }

21

22 String Name;

23

24 }

经过GJC的词法分析程序的分析,转变为如下的单词符号流:

EOF PUBLIC CLASS IDENTIFIER LBRACE PUBLIC IDENTIFIER LPAREN RPAREN LBRACE IDENTIFIER EQ NEW IDENTIFIER LPAREN STRINGLITERAL RPAREN SEMI RBRACE PUBLIC INT IDENTIFIER LPAREN INT IDENTIFIER COMMA INT IDENTIFIER RPAREN LBRACE INT IDENTIFIER SEMI IDENTIFIER EQ IDENTIFIER PLUS IDENTIFIER SEMI RETURN IDENTIFIER SEMI RBRACE PUBLIC VOID IDENTIFIER LPAREN RPAREN LBRACE INT IDENTIFIER EQ INTLITERAL SEMI FOR LPAREN INT IDENTIFIER EQ INTLITERAL SEMI IDENTIFIER LTEQ INTLITERAL SEMI IDENTIFIER PLUSPLUS RPAREN IDENTIFIER PLUSEQ IDENTIFIER SEMI RBRACE IDENTIFIER IDENTIFIER SEMI RBRACE

GJC开始运行时,token被初始化为0,也就是输出的EOF。GJC第一个识别出来的单词符号是PUBLIC,也就是说,Java源程序中1-6行的注释已经被GJC的词法分析程序过滤掉了。Java源程序中第7行的“public class TestCompiler {”被转化为“PUBLIC CLASS IDENTIFIER LBRACE”。Java源程序中第8行的“public TestCompiler(){”被转化为“PUBLIC IDENTIFIER LPAREN RPAREN LBRACE”。下面的转化过程完全类似,不再累述。

我们使用调试器来跟踪程序的执行,在输出第一个单词符号IDENTIFIER(黑体标出)后,在scanChar()方法处设置断点(设置断点的方法详见附录三),就可以很清楚地观察到GJC运行时,各个方法之间的调用关系。

[1] com.sun.tools.javac.v8.parser.Scanner.scanChar (Scanner.java:270)

[2] com.sun.tools.javac.v8.parser.Scanner.nextToken (Scanner.java:1,085)

[3] com.sun.tools.javac.v8.parser.Parser.ident (Parser.java:276)

[4] com.sun.tools.javac.v8.parser.Parser.classDeclaration (Parser.java:1,870)

[5] com.sun.tools.javac.v8.parser.Parser.classOrInterfaceDeclaration (Parser.java:1,854)

[6] com.sun.tools.javac.v8.parser.Parser.typeDeclaration (Parser.java:1,840)

[7] https://www.360docs.net/doc/6718220903.html,pilationUnit (Parser.java:1,788)

[8] com.sun.tools.javac.v8.JavaCompiler.parse (JavaCompiler.java:222)

[9] com.sun.tools.javac.v8.JavaCompiler.parse (JavaCompiler.java:247)

[10] https://www.360docs.net/doc/6718220903.html,pile (JavaCompiler.java:335)

[11] https://www.360docs.net/doc/6718220903.html,pile (Main.java:569)

[12] https://www.360docs.net/doc/6718220903.html,pile (Main.java:41)

[13] com.sun.tools.javac.Main.main (Main.java:28)

从这一刻的输出我们可以观察到如下几点:

?[1][2]是词法分析部分实现的方法,它们完成的功能分别是取一个字符和取一个单词符号。

?词法分析程序对于语法分析程序的主接口是nextToken()方法。

?语法分析程序和词法分析程序之间的关系是调用和被调用的关系,所以语法分析和词法分析在GJC中是一遍完成。

?另外,语法分析部分使用的是递归子程序法(递归下降分析法),这个特点在这个输出中展现得也很明显,关于这个方法将在下一章中介绍。

3.0 词法分析程序小结

GJC采用java语言手工编码实现了一个针对java语言的词法分析程序,其结构简单、

清晰,实现了词法分析程序应该完成的功能。本书的实验三要求用工具来为GJC生成一个词法分析程序,读者可以比较一下工具生成的与手工编写的词法分析程序之间的差异。

编译原理实验--词法分析器

编译原理实验--词法分析器 实验一词法分析器设计 【实验目的】 1(熟悉词法分析的基本原理,词法分析的过程以及词法分析中要注意的问题。 2(复习高级语言,进一步加强用高级语言来解决实际问题的能力。 3(通过完成词法分析程序,了解词法分析的过程。 【实验内容】 用C语言编写一个PL/0词法分析器,为语法语义分析提供单词,使之能把输入的字符 串形式的源程序分割成一个个单词符号传递给语法语义分析,并把分析结果(基本字, 运算符,标识符,常数以及界符)输出。 【实验流程图】

【实验步骤】 1(提取pl/0文件中基本字的源代码 while((ch=fgetc(stream))!='.') { int k=-1; char a[SIZE]; int s=0; while(ch>='a' && ch<='z'||ch>='A' && ch<='Z') { if(ch>='A' && ch<='Z') ch+=32; a[++k]=(char)ch; ch=fgetc(stream); } for(int m=0;m<=12&&k!=-1;m++) for(int n=0;n<=k;n++) {

if(a[n]==wsym[m][n]) ++s; else s=0; if(s==(strlen(wsym[m]))) {printf("%s\t",wsym[m]);m=14;n=k+1;} } 2(提取pl/0文件中标识符的源代码 while((ch=fgetc(stream))!='.') { int k=-1; char a[SIZE]=" "; int s=0; while(ch>='a' && ch<='z'||ch>='A' && ch<='Z') { if(ch>='A' && ch<='Z') ch+=32; a[++k]=(char)ch; ch=fgetc(stream); } for(int m=0;m<=12&&k!=-1;m++) for(int n=0;n<=k;n++) { if(a[n]==wsym[m][n]) ++s; else s=0; if(s==(strlen(wsym[m]))) {m=14;n=k+1;} } if(m==13) for(m=0;a[m]!=NULL;m++) printf("%c ",a[m]);

编译原理实验报告实验一编写词法分析程序

编译原理实验报告实验名称:实验一编写词法分析程序 实验类型:验证型实验 指导教师:何中胜 专业班级:13软件四 姓名:丁越 学号: 电子邮箱: 实验地点:秋白楼B720 实验成绩: 日期:2016年3 月18 日

一、实验目的 通过设计、调试词法分析程序,实现从源程序中分出各种单词的方法;熟悉词法分析 程序所用的工具自动机,进一步理解自动机理论。掌握文法转换成自动机的技术及有穷自动机实现的方法。确定词法分析器的输出形式及标识符与关键字的区分方法。加深对课堂教学的理解;提高词法分析方法的实践能力。通过本实验,应达到以下目标: 1、掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。 2、掌握词法分析的实现方法。 3、上机调试编出的词法分析程序。 二、实验过程 以编写PASCAL子集的词法分析程序为例 1.理论部分 (1)主程序设计考虑 主程序的说明部分为各种表格和变量安排空间。 数组 k为关键字表,每个数组元素存放一个关键字。采用定长的方式,较短的关键字 后面补空格。 P数组存放分界符。为了简单起见,分界符、算术运算符和关系运算符都放在 p表中 (编程时,还应建立算术运算符表和关系运算符表,并且各有类号),合并成一类。 id和ci数组分别存放标识符和常数。 instring数组为输入源程序的单词缓存。 outtoken记录为输出内部表示缓存。 还有一些为造表填表设置的变量。 主程序开始后,先以人工方式输入关键字,造 k表;再输入分界符等造p表。 主程序的工作部分设计成便于调试的循环结构。每个循环处理一个单词;接收键盘上 送来的一个单词;调用词法分析过程;输出每个单词的内部码。 ⑵词法分析过程考虑 将词法分析程序设计成独立一遍扫描源程序的结构。其流程图见图1-1。 图1-1 该过程取名为 lexical,它根据输入单词的第一个字符(有时还需读第二个字符),判断单词类,产生类号:以字符 k表示关键字;i表示标识符;c表示常数;p表示分界符;s表示运算符(编程时类号分别为 1,2,3,4,5)。 对于标识符和常数,需分别与标识符表和常数表中已登记的元素相比较,如表中已有 该元素,则记录其在表中的位置,如未出现过,将标识符按顺序填入数组id中,将常数 变为二进制形式存入数组中 ci中,并记录其在表中的位置。 lexical过程中嵌有两个小过程:一个名为getchar,其功能为从instring中按顺序取出一个字符,并将其指针pint加1;另一个名为error,当出现错误时,调用这个过程, 输出错误编号。 2.实践部分

编译原理词法分析器语法分析器实验报告

编译技术 班级网络0802 学号3080610052姓名叶晨舟 指导老师朱玉全2011年 7 月 4 日

一、目的 编译技术是理论与实践并重的课程,而其实验课要综合运用一、二年级所学的多门课程的内容,用来完成一个小型编译程序。从而巩固和加强对词法分析、语法分析、语义分析、代码生成和报错处理等理论的认识和理解;培养学生对完整系统的独立分析和设计的能力,进一步培养学生的独立编程能力。 二、任务及要求 基本要求: 1.词法分析器产生下述小语言的单词序列 这个小语言的所有的单词符号,以及它们的种别编码和内部值如下表: 单词符号种别编码助记符内码值 DIM IF DO STOP END 标识符 常数(整)= + * ** , ( )1 2 3 4 5 6 7 8 9 10 11 12 13 14 $DIM $IF $DO $STOP $END $ID $INT $ASSIGN $PLUS $STAR $POWER $COMMA $LPAR $RPAR - - - - - - 内部字符串 标准二进形式 - - - - - - 对于这个小语言,有几点重要的限制: 首先,所有的关键字(如IF﹑WHILE等)都是“保留字”。所谓的保留字的意思是,用户不得使用它们作为自己定义的标示符。例如,下面的写法是绝对禁止的: IF(5)=x 其次,由于把关键字作为保留字,故可以把关键字作为一类特殊标示符来处理。也就是说,对于关键字不专设对应的转换图。但把它们(及其种别编码)预先安排在一张表格中(此表叫作保留字表)。当转换图识别出一个标识符时,就去查对这张表,确定它是否为一个关键字。 再次,如果关键字、标识符和常数之间没有确定的运算符或界符作间隔,则必须至少用一个空白符作间隔(此时,空白符不再是完全没有意义的了)。例如,一个条件语句应写为

编译原理词法分析和语法分析报告+代码(C语言版)

词法分析 一、实验目的 设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。 二、实验要求 2.1 待分析的简单的词法 (1)关键字: begin if then while do end 所有的关键字都是小写。 (2)运算符和界符 : = + - * / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(SUM),通过以下正规式定义: ID = letter (letter | digit)* NUM = digit digit* (4)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。 2.2 各种单词符号对应的种别码: 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 例如:对源程序begin x:=9: if x>9 then x:=2*x+1/3; end #的源文件,经过词法分析后输出如下序列: (1,begin)(10,x)(18,:=)(11,9)(26,;)(2,if)…… 三、词法分析程序的算法思想: 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。

3.1 主程序示意图: 主程序示意图如图3-1所示。其中初始包括以下两个方面: ⑴关键字表的初值。 关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下: Char *rwtab[6] = {“begin”, “if”, “then”, “while”, “do”, “end”,}; 图3-1 (2)程序中需要用到的主要变量为syn,token和sum 3.2 扫描子程序的算法思想: 首先设置3个变量:①token用来存放构成单词符号的字符串;②sum用来整型单词;③syn用来存放单词符号的种别码。扫描子程序主要部分流程如图3-2所示。

编译原理词法分析实验报告

词法分析器实验报告 一、实验目的 选择一种编程语言实现简单的词法分析程序,设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。 二、实验要求 待分析的简单的词法 (1)关键字: begin if then while do end 所有的关键字都是小写。 (2)运算符和界符 : = + - * / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(SUM),通过以下正规式定义: ID = letter (letter | digit)* NUM = digit digit* (4)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。 各种单词符号对应的种别码: 表各种单词符号对应的种别码 词法分析程序的功能: 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 例如:对源程序begin x:=9: if x>9 then x:=2*x+1/3; end #的源文件,经过词法分析后输出如下序列: (1,begin)(10,x)(18,:=)(11,9)(26,;)(2,if)…… 三、词法分析程序的算法思想: 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根

据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。 主程序示意图: 主程序示意图如图3-1所示。其中初始包括以下两个方面: ⑴关键字表的初值。 关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下: Char *rwtab[6] = {“begin”, “if”, “then”, “while”, “do”, “end”,}; 图3-1 (2)程序中需要用到的主要变量为syn,token和sum 扫描子程序的算法思想: 首先设置3个变量:①token用来存放构成单词符号的字符串;②sum用来整型单词;③syn 用来存放单词符号的种别码。扫描子程序主要部分流程如图3-2所示。

编译原理实验词法分析实验报告

编译技术实验报告 实验题目:词法分析 学院:信息学院 专业:计算机科学与技术学号: 姓名:

一、实验目的 (1)理解词法分析的功能; (2)理解词法分析的实现方法; 二、实验内容 PL0的文法如下 …< >?为非终结符。 …::=? 该符号的左部由右部定义,可读作“定义为”。 …|? 表示…或?,为左部可由多个右部定义。 …{ }? 表示花括号内的语法成分可以重复。在不加上下界时可重复0到任意次 数,有上下界时可重复次数的限制。 …[ ]? 表示方括号内的成分为任选项。 …( )? 表示圆括号内的成分优先。 上述符号为“元符号”,文法用上述符号作为文法符号时需要用引号…?括起。 〈程序〉∷=〈分程序〉. 〈分程序〉∷= [〈变量说明部分〉][〈过程说明部分〉]〈语句〉 〈变量说明部分〉∷=V AR〈标识符〉{,〈标识符〉}:INTEGER; 〈无符号整数〉∷=〈数字〉{〈数字〉} 〈标识符〉∷=〈字母〉{〈字母〉|〈数字〉} 〈过程说明部分〉∷=〈过程首部〉〈分程序〉{;〈过程说明部分〉}; 〈过程首部〉∷=PROCEDURE〈标识符〉; 〈语句〉∷=〈赋值语句〉|〈条件语句〉|〈过程调用语句〉|〈读语句〉|〈写语句〉|〈复合语句〉|〈空〉 〈赋值语句〉∷=〈标识符〉∶=〈表达式〉 〈复合语句〉∷=BEGIN〈语句〉{;〈语句〉}END 〈条件〉∷=〈表达式〉〈关系运算符〉〈表达式〉 〈表达式〉∷=〈项〉{〈加法运算符〉〈项〉} 〈项〉∷=〈因子〉{〈乘法运算符〉〈因子〉} 〈因子〉∷=〈标识符〉|〈无符号整数〉|'('〈表达式〉')' 〈加法运算符〉∷=+|- 〈乘法运算符〉∷=* 〈关系运算符〉∷=<>|=|<|<=|>|>= 〈条件语句〉∷=IF〈条件〉THEN〈语句〉 〈字母〉∷=a|b|…|X|Y|Z 〈数字〉∷=0|1|2|…|8|9 实现PL0的词法分析

编译原理词法分析和语法分析报告 代码(C语言版)

词法分析 三、词法分析程序的算法思想: 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。 3.1 主程序示意图: 扫描子程序主要部分流程图 其他

词法分析程序的C语言程序源代码: // 词法分析函数: void scan() // 数据传递: 形参fp接收指向文本文件头的文件指针; // 全局变量buffer与line对应保存源文件字符及其行号,char_num保存字符总数。 void scan() { char ch; int flag,j=0,i=-1; while(!feof(fp1)) { ch=fgetc(fp1); flag=judge(ch); printf("%c",ch);//显示打开的文件 if(flag==1||flag==2||flag==3) {i++;buffer[i]=ch;line[i]=row;} else if(flag==4) {i++;buffer[i]='?';line[i]=row;} else if(flag==5) {i++;buffer[i]='~';row++;} else if(flag==7) continue; else cout<<"\n请注意,第"<

编译原理C语言词法分析器

编译原理 C语言词法分析器 一、实验题目 编制并调试C词法分析程序。 a.txt源代码: ?main() { int sum=0 ,it=1;/* Variable declaration*/ if (sum==1) it++; else it=it+2; }? 设计其词法分析程序,能识别出所有的关键字、标识符、常数、运算符(包括复合运算符,如++)、界符;能过滤掉源程序中的注释、空格、制表符、换行符;并且能够对一些词法规则的错误进行必要的处理,如:标识符只能由字母、数字和下划线组成,且第一个字符必须为字母或下划线。实验要求:要给出所分析语言的词法说明,相应的状态转换图,单词的种别编码方案,词法分析程序的主要算法思想等。 二、实验目的 1、理解词法分析在编译程序中的作用; 2、掌握词法分析程序的实现方法和技术; 3、加深对有穷自动机模型的理解。 三、主要函数 四、设计 1.主函数void main ( )

2. 初始化函数void load ( ) 3. 保留字及标识符判断函数void char_search(char *word) 4. 整数类型判断函数void inta_search(char *word) 5. 浮点类型判断函数void intb_search(char *word)

6. 字符串常量判断函数void cc_search(char *word) 7. 字符常量判断函数void c_search(char *word) 同4、5函数图 8.主扫描函数void scan ( ) 五、关键代码 #include #include

编译原理实验报告(词法分析器语法分析器)

编译原理实验报告

实验一 一、实验名称:词法分析器的设计 二、实验目的:1,词法分析器能够识别简单语言的单词符号 2,识别出并输出简单语言的基本字.标示符.无符号整数.运算符.和界符。 三、实验要求:给出一个简单语言单词符号的种别编码词法分析器 四、实验原理: 1、词法分析程序的算法思想 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。 2、程序流程图 (1 (2)扫描子程序

3

五、实验内容: 1、实验分析 编写程序时,先定义几个全局变量a[]、token[](均为字符串数组),c,s( char型),i,j,k(int型),a[]用来存放输入的字符串,token[]另一个则用来帮助识别单词符号,s用来表示正在分析的字符。字符串输入之后,逐个分析输入字符,判断其是否‘#’,若是表示字符串输入分析完毕,结束分析程序,若否则通过int digit(char c)、int letter(char c)判断其是数字,字符还是算术符,分别为用以判断数字或字符的情况,算术符的判断可以在switch语句中进行,还要通过函数int lookup(char token[])来判断标识符和保留字。 2 实验词法分析器源程序: #include #include #include int i,j,k; char c,s,a[20],token[20]={'0'}; int letter(char s){ if((s>=97)&&(s<=122)) return(1); else return(0); } int digit(char s){ if((s>=48)&&(s<=57)) return(1); else return(0); } void get(){ s=a[i]; i=i+1; } void retract(){ i=i-1; } int lookup(char token[20]){ if(strcmp(token,"while")==0) return(1); else if(strcmp(token,"if")==0) return(2); else if(strcmp(token,"else")==0) return(3); else if(strcmp(token,"switch")==0) return(4); else if(strcmp(token,"case")==0) return(5); else return(0); } void main() { printf("please input string :\n"); i=0; do{i=i+1; scanf("%c",&a[i]);

编译原理实验报告一 简单样本语言的词法分析器

理工大学信息工程与自动化学院学生实验报告 (2012 —2013学年第一学期) 一、实验目的及容 编译技术是理论与实践并重的课程,而其实验课要综合运用所学的多门课程的容,用来完成一个小型编译程序。从而巩固和加强对词法分析、语法分析、语义分析、代码生成和报错处理等理论的认识和理解;培养学生对完整系统的独立分析和设计的能力,进一步培养学生的独立编程能力。 调试并完成一个词法分析程序,加深对词法分析原理的理解。 二、实验原理及基本技术路线图(框原理图或程序流程图) 1、待分析的简单语言的词法 (1)关键字: begin if then while do end 所有关键字都是小写。 (2)运算符和界符: := + –* / < <= <> > >= = ; ( ) #

(3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码 3、词法分析程序的功能 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 二、所用仪器、材料(设备名称、型号、规格等或使用软件)

1台PC以及VISUAL C++6.0软件。 三、实验法、步骤(或:程序代码或操作过程) (1)程序代码: #include #include #include char prog[80],token[8]; char ch; int syn,p,m=0,n,row,sum=0; char *rwtab[6]={"begin","if","then","while","do","end"}; void scaner() { for(n=0;n<8;n++) token[n]=NULL; ch=prog[p++]; while(ch==' ') { ch=prog[p]; p++; } if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) { m=0; while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) { token[m++]=ch; ch=prog[p++]; } token[m++]='\0'; p--; syn=10; for(n=0;n<6;n++)

编译原理实验词法分析语法分析

本代码只供学习参考: 词法分析源代码: #include #include #include using namespace std; string key[8]={"do","end","for","if","printf","scanf","then","while"}; string optr[4]={"+","-","*","/"}; string separator[6]={",",";","{","}","(",")"}; char ch; //判断是否为保留字 bool IsKey(string ss) { int i; for(i=0;i<8;i++) if(!strcmp(key[i].c_str(),ss.c_str())) return true; return false; } //字母判断函数 bool IsLetter(char c) { if(((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z'))) return true; return false; } //数字判断函数 bool IsDigit(char c) { if(c>='0'&&c<='9') return true; return false; } //运算符判断函数 bool IsOptr(string ss) { int i; for(i=0;i<4;i++) if(!strcmp(optr[i].c_str(),ss.c_str())) return true ; return false; } //分界符判断函数 bool IsSeparator(string ss) { int i; for(i=0;i<6;i++) if(!strcmp(separator[i].c_str(),ss.c_str()))

编译原理实验报告2词法分析程序的设计

实验2 词法分析程序的设计 一、实验目的 掌握计算机语言的词法分析程序的开发方法。 二、实验内容 编制一个能够分析三种整数、标识符、主要运算符和主要关键字的词法分析程序。 三、实验要求 1、根据以下的正规式,编制正规文法,画出状态图; 标识符<字母>(<字母>|<数字字符>)* 十进制整数0 | ((1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*) 八进制整数0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)* 十六进制整数0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)* 运算符和界符+ - * / > < = ( ) ; 关键字if then else while do 2、根据状态图,设计词法分析函数int scan( ),完成以下功能: 1)从文本文件中读入测试源代码,根据状态转换图,分析出一个单词, 2)以二元式形式输出单词<单词种类,单词属性> 其中单词种类用整数表示: 0:标识符 1:十进制整数 2:八进制整数 3:十六进制整数 运算符和界符,关键字采用一字一符,不编码 其中单词属性表示如下: 标识符,整数由于采用一类一符,属性用单词表示 运算符和界符,关键字采用一字一符,属性为空 3、编写测试程序,反复调用函数scan( ),输出单词种别和属性。 四、实验环境 PC微机 DOS操作系统或Windows 操作系统 Turbo C 程序集成环境或Visual C++ 程序集成环境 五、实验步骤 1、根据正规式,画出状态转换图;

编译原理词法分析器语法分析课程设计报告书

《编译原理》 课程设计 院系信息科学与技术学院 专业软件工程 年级 2011级 学号 20112723 姓名林苾湲 西南交通大学信息科学与技术学院 2013年 12月

目录 课程设计1 词法分析器 (2) 1.1 设计题目 (2) 1.2 设计容 (2) 1.3 设计目的 (2) 1.4 设计环境 (2) 1.5 需求分析 (2) 1.6 概要设计 (2) 1.7 详细设计 (4) 1.8 编程调试 (5) 1.9 测试 (11) 1.10 结束语 (13) 课程设计2 赋值语句的解释程序设计 (14) 2.1 设计题目 (14) 2.2 设计容 (14) 2.3 设计目的 (14) 2.4 设计环境 (14) 2.5 需求分析 (15) 2.6 概要设计 (16) 2.7 详细设计 (16) 2.8 编程调试 (24) 2.9 测试 (24) 2.10 结束语 (25)

课程设计一词法分析器设计 一、设计题目 手工设计c语言的词法分析器(可以是c语言的子集)。 二、设计容 处理c语言源程序,过滤掉无用符号,判断源程序中单词的合法性,并分解出正确的单词,以二元组形式存放在文件中。 三、设计目的 了解高级语言单词的分类,了解状态图以及如何表示并识别单词规则,掌握状态图到识别程序的编程。 四、设计环境 该课程设计包括的硬件和软件条件如下: 4.1.硬件 (1)Intel Core Duo CPU P8700 (2)存4G 4.2.软件 (1)Window 7 32位操作系统 (2)Microsoft Visual Studio c#开发平台 4.3.编程语言 C#语言 五、需求分析 5.1.源程序的预处理:源程序中,存在许多编辑用的符号,他们对程序逻辑功能无任何影响。例如:回车,换行,多余空白符,注释行等。在词法分析之前,首先要先剔除掉这些符号,使得词法分析更为简单。 5.2.单词符号的识别并判断单词的合法性:将每个单词符号进行不同类别的划分。单词符号可以划分成5中。 (1)标识符:用户自己定义的名字,常量名,变量名和过程名。 (2)常数:各种类型的常数。 (3) 保留字(关键字):如if、else、while、int、float等。 (4) 运算符:如+、-、*、<、>、=等。 (5)界符:如逗号、分号、括号等。 5.3.将所有合法的单词符号转化为便于计算机处理的二元组形式:(单词分类号,单词自身值);以图形化界面显示出来。 5.4.可选择性地将结果保存到文件中。 六、概要设计 6.1.数据类型 6.1.1.单词的分类:本词法分析器演示的是C语言的一个子集,故字符集如下:

编译原理词法分析器

一、实验目的 了解词法分析程序的两种设计方法:1.根据状态转换图直接编程的方式;2.利用DFA 编写通用的词法分析程序。 二、实验内容及要求 1.根据状态转换图直接编程 编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号)流文件输出。在此,词法分析程序作为单独的一遍,如下图所示。 具体任务有: (1)组织源程序的输入 (2)拼出单词并查找其类别编号,形成二元式输出,得到单词流文件 (3)删除注释、空格和无用符号 (4)发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。 (5)对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则返回0并且填写符号表或常量表。 标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址 注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。 常量表结构:常量名,常量值 2.编写DFA模拟程序 算法如下: DFA(S=S0,MOVE[][],F[],ALPHABET[]) /*S为状态,初值为DFA的初态,MOVE[][]为状态转换矩阵,F[] 为终态集,ALPHABET[] 为字母表,其中的字母顺序与MOVE[][] 中列标题的字母顺序一致。*/ { Char Wordbuffer[10]=“”//单词缓冲区置空 Nextchar=getchar();//读 i=0; while(nextchar!=NULL)//NULL代表此类单词 { if (nextcha r!∈ALPHABET[]){ERROR(“非法字符”),return(“非法字符”);} S=MOVE[S][nextchar] //下一状态 if(S=NULL)return(“不接受”);//下一状态为空,不能识别,单词错误 wordbuffer[i]=nextchar ;//保存单词符号 i++; nextchar=getchar(); } Wordbuffer[i]=‘\0’;

编译原理实验 词法分析&语法分析程序

编译原理实验 词 法 分 析 程 序

实验一:词法分析程序 1、实验目的 从左至右逐个字符的对源程序进行扫描,产生一个个单词符号,把字符串形式的源程序改造成单词符号形式的中间程序。 2、实验内容 表C语言子集的单词符号及内码值 单词符号种别编码助记符内码值 while 1 while -- if 2 if -- else 3 else -- switch 4 switch -- case 5 case -- 标识符 6 id id在符号表中的位置 常数7 num num在常数表中的位置 + 8 + -- - 9 - -- * 10 * -- <= 11 relop LE < 11 relop LT == 11 relop LQ = 12 = -- ; 13 ; -- 输入源程序如下 if a==1 a=a+1; else a=a+2; 输出对应的单词符号形式的中间程序 3、实验过程 实验上机程序如下: #include "stdio.h" #include "string.h" int i,j,k; char s ,a[20],token[20]; int letter() { if((s>=97)&&(s<=122))return 1; else return 0; } int Digit() {if((s>=48)&&(s<=57))return 1;

else return 0; } void get() { s=a[i]; i=i+1; } void retract() {i=i-1;} int lookup() { if(strcmp(token, "while")==0) return 1; else if(strcmp(token, "if")==0) return 2; else if(strcmp(token,"else")==0) return 3; else if(strcmp(token,"switch")==0) return 4; else if(strcmp(token,"case")==0) return 5; else return 0; } void main() { printf("please input you source program,end('#'):\n"); i=0; do { i=i+1; scanf("%c",&a[i]); }while(a[i]!='#'); i=1; memset(token,0,sizeof(char)*10); j=0; get(); while(s!='#') { if(s==' '||s==10||s==13) get(); else { switch(s)

编译原理实验-词法分析器的设计与实现.docx

南华大学 计算机科学与技术学院实验报告 (2018~2019学年度第二学期) 课程名称编译原理 实验名称词法分析器的设计与 实现 姓名学号 专业班级 地点教师

1.实验目的及要求 实验目的 加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。 实验要求 1.对单词的构词规则有明确的定义; 2.编写的分析程序能够正确识别源程序中的单词符号; 3.识别出的单词以<种别码,值>的形式保存在符号表中,正确设计和维护 符号表; 4.对于源程序中的词法错误,能够做出简单的错误处理,给出简单的错误 提示,保证顺利完成整个源程序的词法分析; 2.实验步骤 1.词法分析规则 <标识符>::=<字母>|<标识符><字母>|<标识符><数字> <常数>::=<数字>|<数字序列><数字> <数字序列>::=<数字序列><数字>|<数字>|<.> <字母>::=a|b|c|……|x|y|z <数字>::=0|1|2|3|4|5|6|7|8|9 <运算符>::=<关系运算符>|<算术运算符>|<逻辑运算符>|<位运算符>|<赋值运算符> <算数运算符>::=+|-|*|/|...|-- <关系运算符>::=<|>|!=|>=|<=|== <逻辑运算符>::=&&| || |! <位运算符>::=&| | |! <赋值运算符>::==|+=|-=|/=|*= <分界符>::=,|;|(|)|{|}|:| // |/**/ <保留字>::=main|if|else|while|do|for|...|void

编译原理词法分析及语法分析

编译原理 实验报告 实验名称:词法分析及语法分析专业班级: 姓名: 学号: 完成日期:

实验一、sample语言的词法分析 一、实验目的 给出SAMPLE文法规范,要求编写SAMPLE语言的词法分析程序。 二、实验准备 了解sample语言单词的定义,选择任一种编程语言实现词法分析。 三、实验内容 给出SAMPLE语言文法,输出单词(关键字、专用符号以及其它标记)。 1、格式 输入:源程序文件。输出:关键字、专用符号以及其它标记。 2、实现原理 程序中先判断这个句语句中每个单元为关键字、常数、运算符、界符,对与不同的单词符号给出不同编码形式的编码,用以区分之。 3、实验方法 读懂Sample源代码,自己重点独立实现对常量的判别。 四、实验设计 1、设计SAMPLE语言的词法分析器 A、字符集定义 1. <字符集> → <字母>│<数字>│<单界符> 2. <字母> → A│B│…│Z│a│b│…│z 3. <数字> → 0│1│2│…│9 4. <单界符> → +│-│*│/│=│<│>│(│)│[│]│:│. │; │, │' B、单词集定义 5.<单词集> → <保留字>│<双界符>│<标识符>│<常数>│<单界符> 6.<保留字> → and│array│begin│bool│call│case│char│constant│dim│do│else │end│false│for│if│input│integer│not│of│or│output│procedure│program │read│real│repeat│set│stop│then│to│true│until│var│while│write 7.<双界符> → <>│<=│>=│:= │/*│*/│.. 8.<标识符> → <字母>│<标识符> <数字>│<标识符> <字母> 9.<常数> → <整数>│<布尔常数>│<字符常数> 10.<整数> → <数字>│<整数> <数字> 11.<布尔常数> → true│false 12.<字符常数> → ' 除 {'} 外的任意字符串 ' 2、词法分析系统流程设计

(完整版)《编译原理》词法分析程序设计方案

实验1-4 《编译原理》S语言词法分析程序设计方案 一、实验目的 了解词法分析程序的两种设计方法:1.根据状态转换图直接编程的方式;2.利用DFA 编写通用的词法分析程序。 二、实验内容 1.根据状态转换图直接编程 编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号)流文件输出。在此,词法分析程序作为单独的一遍,如下图所示。 具体任务有: (1)组织源程序的输入 (2)拼出单词并查找其类别编号,形成二元式输出,得到单词流文件 (3)删除注释、空格和无用符号 (4)发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。 (5)对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则返回0并且填写符号表或常量表。 标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址 注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。 常量表结构:常量名,常量值 2.编写DFA模拟程序 算法如下: DFA(S=S0,MOVE[][],F[],ALPHABET[]) /*S为状态,初值为DFA的初态,MOVE[][]为状态转换矩阵,F[] 为终态集,ALPHABET[] 为字母表,其中的字母顺序与MOVE[][] 中列标题的字母顺序一致。*/ { Char Wordbuffer[10]=“”//单词缓冲区置空 Nextchar=getchar();//读 i=0; while(nextchar!=NULL)//NULL代表此类单词 { if (nextcha r!∈ALPHABET[]){ERROR(“非法字符”),return(“非法字符”);} S=MOVE[S][nextchar] //下一状态 if(S=NULL)return(“不接受”);//下一状态为空,不能识别,单词错误 wordbuffer[i]=nextchar ;//保存单词符号 i++; nextchar=getchar(); } Wordbuffer[i]=‘\0’; If(S∈F)return(wordbuffer);//接受 Else return(“不接受”);

编译原理词法分析器实验报告

北华航天工业学院 《编译原理》课程实验报告 课程实验题目:词法分析器实验 作者所在系部:计算机科学与工程系作者所在专业:计算机科学与技术 作者所在班级:B08512 作者学号:18 作者姓名:李桂丁 指导教师姓名:李建义 完成时间:2010年3月26日

一、实验目的 了解词法分析程序的两种设计方法:1.根据状态转换图直接编程的方式;2.利用DFA 编写通用的词法分析程序。 二、实验内容及要求 1.根据状态转换图直接编程 编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号)流文件输出。在此,词法分析程序作为单独的一遍,如下图所示。 具体任务有: (1)组织源程序的输入 (2)拼出单词并查找其类别编号,形成二元式输出,得到单词流文件 (3)删除注释、空格和无用符号 (4)发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。 (5)对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则返回0并且填写符号表或常量表。 标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址 注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。 常量表结构:常量名,常量值 2.能对任何S语言源程序进行分析 在运行词法分析程序时,应该用问答形式输入要被分析的S源语言程序的文件名,然后对该程序完成词法分析任务。 3.能检查并处理某些词法分析错误 词法分析程序能给出的错误信息包括:总的出错个数,每个错误所在的行号,错误的编号及错误信息。 4. 本实验要求处理以下两种错误(编号分别为1,2): 1:非法字符:单词表中不存在的字符处理为非法字符,处理方式是删除该字符,给出错误信息,“某某字符非法”。 2:源程序文件结束而注释未结束。注释格式为:/* …… */

编译原理词法分析器

编译原理大作业词法分析器 班级:电计0902 学号:200981174 姓名:修德斌

一、实验目的 通过设计、调试词法分析程序,实现从源程序中分出各种单词的方法;熟悉词法分析程序所用的工具自动机,进一步理解自动机理论。掌握文法转换成自动机的技术及有穷自动机实现的方法。确定词法分析器的输出形式及标识符与关键字的区分方法。加深对课堂教学的理解;提高词法分析方法的实践能力。通过本实验,应达到以下目标: 1、掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。 2、掌握词法分析的实现方法。 3、上机调试编出的词法分析程序。 二、实验过程 1、需求分析: 词法分析是编译程序的第一个阶段,主要任务是对于字符串流的输入,根据词表,将关键字、变量等转化成自定义逻辑结构,就是输入源程序,输出单词符号,用于下一步的语法分析。 比如: { int a; int b; { a=10; if (a>0) then b=a a and b; } } 词法分析的功能就是输入源程序,输出单词符号,去除空白符等无意义字符,然后对于像main、a、b这样的函数名、变量名字符串参考前后关键字,按照各自的分类,转换成一个变量表,对于像char = +这种关键字,按照关键词词表转化成对应的序号。 2、概要设计: (1)、输入输出方式: 以文件的形式进行输入,然后对识别出的单词以 Linenum: 行数 string= 具体的单词或者符号类型(如分解符等) 最后在识别完成后列出了标示符的个数以及标示符的表示名称 关键字表: sting key[6] key[0]=”if” key[1]=”else” key[2]=”while” key[3]=”do”

相关文档
最新文档