一.实验目的●了解编译器的词法分析器的作用。
其作用是源程序转化为便于编译程序其余部分进行处理的内部格式。
●了解词法分析器的任务。
识别出源程序中的各个基本语法单位;删除无用的空白字符,回车以及其他与输入介质相关的非实质性的字符,以及注释等。
一.实验说明●词法分析程序:词法分析程序完成的是编译第一阶段的工作。
词法分析工作可以是独立的一遍,把字符流的源程序变为单词序列,输出在一个中间文件上,这个文件做为语法分析程序的输入而继续编译过程。
然而,更一般的情况,是将词法分析程序设计成一个子程序,每当语法分析程序需要一个单词时,则调用该子程序。
词法分析程序每得到一次调用,便从源程序文件中读入一些字符,直到识别出一个单词,或说直到下一单词的第一个字符为止。
●词法分析程序的主要功能是从字符流的源程序中识别单词,它要从左至右逐个字符地扫描源程序,因此它还可完成其它一些任务。
比如,滤掉源程序中的注释和空白(由空格,制表或回车换行字符引起的空白);又比如,为了使编译程序能将发现的错误信息与源程序的出错位置联系起来,词法分析程序负责记录新读入的字符行的行号,以便行号与出错信息相联;再有,在支持宏处理功能的源语言中,可以由词法分析程序完成其预处理等等。
●以下是五种单词符号:- 保留字,关键字- 标识符- 常数(量)- 运算符- 界符●词法分析程序所输出的单词符号常常采用以下二元式表示:(单词种别,单词自身的值)。
单词的种别是语法分析需要的信息,而单词自身的值则是编译其它阶段需要的信息。
比如在PASCAL的语句const i=25, yes=1;中的单词25和1的种别都是常数,常数的值25和1对于代码生成来说,是必不可少的。
有时,对某些单词来说,不仅仅需要它的值,还需要其它一些信息以便编译的进行。
比如,对于标识符来说,还需要记载它的类别、层次还有其它属性,如果这些属性统统收集在符号表中,那么可以将单词的二元式表示设计成如下形式(标识符,指向该标识符所在符号表中位置的指针),如上述语句中的单词i和yes的表示为:(标识符,指向i的表项的指针)(标识符,指向yes的表项的指针)●实际上,词法也是语法的一部分,词法描述完全可以归并到语法描述中去,只不过词法规则更简单些。
这在后面的章节中可以看到。
为什么将词法分析做为一个独立的阶段?为什么把编译过程的分析工作划分成词法分析和语法分析两个阶段?主要的考虑因素为:①使整个编译程序的结构更简洁、清晰和条理化。
②编译程序的效率会改进。
③增强编译程序的可移植性。
二.实验要求对于如下文法所定义的PASCAL语言子集,试编写一个词法分析程序:<程序>—><变量说明>BEGIN<词句表>END。
<变量说明>—><变量>,<变量>|<变量>。
<类型>—>INTEGER。
………………(1)。
单词的分类:可将所有标志符归为一类;将常数归为一类;保留字和分隔符则可采取一词一类。
(2)。
符号表的建立:可事先建立一保留字表,以备在识别保留字时进行查询。
变量名及常数表则在词法分析过程中建立。
(3)。
单词串的输出形式:所有输出单词都用二元组形式。
四.程序设计思路●实现语言:JA V A●从源文件读入源代码字符然后进行扫描,第一次扫描去空格,注释,换行,回车等,分离出数字,关键字,标志符;第二次扫描分离出标点与运算符。
●经过两次扫描分离出四种类型的单词符号,把关键字,标志符,特殊符号,数字分别保存在一个表中。
●显示结果●由于采用两次扫描,所以输出结果与书上不同。
先关键字,标志符,数字三者然后特殊符号。
流程图如下:五.程序代码package Analyser;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.io.*;import java.util.*;public class Analyser extends JFrame implements Runnable{//保留关键字的数组private static final String keyWord[]={"PROGRAM","V AR","BEGIN", "END", "integer", "DIV"};private JTextArea displayTA,resultTA;private JFileChooser filechooser;private JButton open,convert;//记录文本的行数private int col;//记录文本内容private String content;//creat GUIprivate HashTable key;//记录关键字的哈希表private HashTable identifer;//记录标志符的哈希表private HashTable digi;//记录数字的哈希表private HashTable token;//记录符号的哈希表public Analyser(){super("扫描器");key=new HashTable ();identifer=new HashTable ();digi=new HashTable ();token=new HashTable ();final Container c=this.getContentPane();JMenuBar mb=new JMenuBar();JMenu help=new JMenu("Help");help.setMnemonic('H');JMenuItem introduction=new JMenuItem("说明");introduction.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){JOptionPane.showMessageDialog(c,"上面文本区显示文件内容,下面文本区显示分析结果");}});help.add(introduction);mb.add(help);displayTA=new JTextArea(12,10);displayTA.setTabSize(1);displayTA.setEditable(false);resultTA=new JTextArea(12,10);filechooser=new JFileChooser();open=new JButton("打开文件");convert=new JButton("开始分析");Box box=Box.createVerticalBox();box.add(new JScrollPane(displayTA));JPanel p=new JPanel();p.add(new JLabel("上面文本区显示文件内容"));p.add(open);p.add(convert);p.add(new JLabel("下面文本区显示分析结果"));box.add(p);box.add(new JScrollPane(resultTA));this.setJMenuBar(mb);this.add(box);open.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){int response=filechooser.showOpenDialog(c);if(response==JFileChooser.CANCEL_OPTION)return;try{//IO流操作,打开文件File file=filechooser.getSelectedFile();FileInputStream fis=new FileInputStream(file);DataInputStream dis=new DataInputStream(fis);String line;//buffer.append(line);displayTA.setText("");//读一行字符while((line=dis.readLine())!=null){displayTA.append(line+"\n");col++;}content=displayTA.getText();dis.close();dis=null;fis.close();fis=null;}catch(Exception ex){ex.printStackTrace();}}});convert.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){if(content==null)return;resultTA.setText("");//启动两次扫描的线程new Thread(Analyser.this).start();// firstscan();// secondscan();// for(int i=0;i<key.size();i++)// {// resultTA.append((String)key.get(i));// }}});this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setSize(500,500);this.setResizable(false);this.setVisible(true);}//第一次扫描,去空格,注释,换行,回车等,分离出数字,关键字,标志符public void firstscan(){StringTokenizer st=new StringTokenizer(content,".:= \n();+-*",false);while(st.hasMoreTokens()){String ch=st.nextToken();resultTA.append("("+ch+" "+judge(ch)+")");}}//第二次扫描分离出标点符号和运算符public void secondscan(){StringTokenizer st=new StringTokenizer(content," \nabcdefghijklmnopqrstuvwxyzQWERTYUIOPASDFGHJKLZXCVBNM123456789_",false);while(st.hasMoreTokens()){String ch=st.nextToken();resultTA.append("("+ch+" 2)"+"\n");}}public boolean isKeyWord(String s){for(int i=0;i<keyWord.length;i++){if(s.equals(keyWord[i]))return true;}return false;}//判断是否数字的函数public boolean isDigi(String s){for(int i=0;i<s.length();i++){if(s.charAt(i)>='9'||s.charAt(i)<='0'){return false;}}return true;}//判断是否标志符public boolean isIdentifier(String s){//如果该字符是关键字if(isKeyWord(s))return false;//判断开头第一个字母if(s.charAt(0)=='_'||((s.charAt(0))>='a'&&s.charAt(0)<='z')||(s.charAt(0)>='A'&&s.charAt(0)<='Z')){for(int i=1;i<s.length();i++){if(s.charAt(i)=='_'||((s.charAt(i))>='a'&&s.charAt(i)<='z')||(s.charAt(i)>='A'&&s.charAt(i)<=' Z')||(s.charAt(i)>='0'&&s.charAt(i)<=9))continue;else{return false;}}return true;}elsereturn false;}public int judge(String s){if(isKeyWord(s))return 1;if(isIdentifier(s))return 3;if(isDigi(s))return 4;return 0;}//线程运行方法public void run() {// TODO Auto-generated method stubthis.firstscan();this.secondscan();}//main methodpublic static void main(String args[]){new Analyser();}}六.实验总结●通过对扫描器程序的学习,了解到扫描器在编译器中的重要地位。