实验一 词法分析器【编译原理】

  • 前言
  • 推荐
  • 实验一 词法分析器
  • 代码1
  • 代码1结果
  • 代码2
  • 代码2结果
  • 高级代码
  • 最后

前言

2023-4-2 13:14:26

以下内容源自《【编译原理】》
仅供学习交流使用

推荐

本文代码
全有自己书写
没有推荐

实验一 词法分析器

题目: 词法分析器

要求:1人一组

1.单词的分类。
可将所有标识符归为一类;
将常数归为另一类;
保留字、算符和分隔符则采取一词一类。
2.符号表的建立。
可事先建立一关键字表,以备在识别关键字时进行查询。
变量名表常数表则在词法分析过程中建立。
3.出错处理
实现错误定位,找出原程序中所有词法错误
4.文件读写
源程序以文件形式读入;
输出以文件形式保存,需输出:1)词法分析的结果 2)符号表文件

5.单词串的输出形式。
所输出的每一单词,均按形如(CLASS,VALUE)的二元式编码。对于变量标识符和常数,CLASS字段为相应的类别码,VALUE字段则是该标识符、常数在其符号表中登记项的序号(要求在变量名表登记项中存放该标识符的字符串,其最大长度为四个字符;常数表登记项中则存放该整数的二进制形式。)。对于保留字和分隔号,由于采用一词一类的编码方式,所以仅需在二元式的CLASS字段上放置相应的单词的类别码,VALUE字段则为“空”。不过,为便于查看由词法分析程序所输出的单词串,也可以在CLASS字段上直接放置单词符号串本身。可以仿照书上图3.3的实现程序的结构来编写上述词法分析程序,但其中的若干语义过程有待于具体编写。

6.过滤无效字符、数值转换、宏展开、预包含处理等(灰色选做)

实习报告内容:

1.目的要求
2.单词分类表
3.单词结构描述(正规式或正规文法)
4.单词状态转换图
5.算法描述
6.程序结构
7.运行结果
8.调试情况
9.设计技巧及体会
10.源程序清单(电子版)

代码1

介绍:

代码1实现了以下功能

加载keywords.txt构建关键词表
如果没有该文件会默认构建
读取input.txt单词串进行词法分析
如果没有该文件会随机生成单词串

最后
输出单词分析结果
并把单词分析的结果写入symbols.txt

代码框架

实验一 词法分析器【编译原理】_java


README.md

s1 词法分析器

    Symbol 符号类

    LexicalAnalyzer 词法分析器

    项目/keywords.txt 输入关键词表

    项目/symbols.txt 输出单词表

    项目/input.txt 输入单词串

Symbol

package s1;

import java.util.Objects;

/**
 * 符号类
 */
public class Symbol {
    private int id;//种别编码
    private String value;//单词符号
    private int code;//内码值 符号表中的索引

    public Symbol() {
    }

    public Symbol(int id) {
        this.id = id;
    }

    public Symbol(String value) {
        this.value = value;
    }

    public Symbol(int id, String value) {
        this.id = id;
        this.value = value;
    }

    public Symbol(int id, int code) {
        this.id = id;
        this.code = code;
    }

    public Symbol(int id, String value, int code) {
        this.id = id;
        this.value = value;
        this.code = code;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Symbol symbol = (Symbol) o;
        return Objects.equals(value, symbol.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }

    public String toStringIdCode(){
        return "Symbol{" +
                "id=" + id +
                ", code=" + code +
                '}';
    }


    @Override
    public String toString() {
        return "Symbol{" +
                "id=" + id +
                ", value='" + value + '\'' +
                ", code=" + code +
                '}';
    }
}

LexicalAnalyzer

package s1;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 词法分析器类
 */
public class LexicalAnalyzer {
    public List<Symbol> keywordList=new ArrayList<>();//关键词表
    public List<Symbol> identifierList=new ArrayList<>();//变量名表
    public List<Symbol> constantList=new ArrayList<>();//常量表
    public List<Symbol> symbolList=new ArrayList<>();//运行中单词表 存放<种类,索引> 根据出现的顺序依次添加

    private String input;//输入的单词串
    private int position;//扫描指针

    public LexicalAnalyzer() {
    }

    public LexicalAnalyzer(String input) {
        this.input = input;
        this.position = 0;
    }

    public void toStringSymbolList(){
        for (Symbol s:symbolList) {
//            System.out.println(s.toStringIdCode());
            System.out.println(s.toString());
        }
    }

    //创建关键字表 根据表3.1实现
    {
        File file = new File("keywords.txt");
        if (file.exists()) {
            System.out.println("keywords.txt文件存在");
            System.out.println("读取文件,加载关键词表");
            loadKeywordList();
        } else {
            System.out.println("keywords.txt文件不存在");
            System.out.println("采用默认的关键词表");
            initKeywordList();
        }
    }

    //读取keywords.txt文件的内容加载到keywordList中
    private void loadKeywordList() {
        try {
            BufferedReader reader = new BufferedReader(new FileReader("keywords.txt"));
            String line;
            while ((line = reader.readLine()) != null) {
                String[] arr = line.split("\\s+");
                keywordList.add(new Symbol(Integer.parseInt(arr[0]), arr[1]));
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //默认的关键词表
    public void initKeywordList(){
        //保留字采用一词一类
        keywordList.add(new Symbol(1,"DIM"));
        keywordList.add(new Symbol(2,"IF"));
        keywordList.add(new Symbol(3,"DO"));
        keywordList.add(new Symbol(4,"STOP"));
        keywordList.add(new Symbol(5,"END"));
        //标识符 二次查询
        keywordList.add(new Symbol(6,"identifierList"));
        //常量表 二次查询
        keywordList.add(new Symbol(7,"constantList"));
        //算符采用一词一类
        keywordList.add(new Symbol(8,"="));
        keywordList.add(new Symbol(9,"+"));
        keywordList.add(new Symbol(10,"*"));
        keywordList.add(new Symbol(11,"**"));
        keywordList.add(new Symbol(12,","));
        keywordList.add(new Symbol(13,"("));
        keywordList.add(new Symbol(14,")"));
    }

    //给所有关键字符号赋内码值(也可以不赋值)
    {
        //取出keywordList的符号,给他赋值其索引
        for (Symbol s:keywordList) {
            s.setCode(keywordList.indexOf(s));
        }
    }


    //根据3,2,4编写

    //字符变量,存放最新读进的源程序字符
    private static char ch;

    private static final char EOF='#';

    // 字符数组,存放构成单词符号的字符串
    private  String strToken;

    //子程序过程,将下一输入字符读到ch中,搜索指示器前移一字符位置。
    public  void getChar() {
        if (position<input.length()){
            ch=input.charAt(position++);
        }else{
            ch=EOF;
        }
    }


    //子程序过程,检查ch中的字符是否为空白。若是,则调用GetChar直至ch中进人一个非空白字符。
    public void getBC(){
        if(ch==' '){
            getChar();
            getBC();//递归
        }
    }

    //子程序过程,将ch中的字符连接到strToken之后。
    public void concat(){
        strToken=strToken.concat(""+ch);
    }

    //布尔函数过程,它们分别判断ch中的字符是否为字母和数字。
    public boolean isLetter(){
        return ch >= 'A' && ch <= 'z';
    }

    public boolean isDigit(){
        return ch >= '0' && ch <= '9';
    }

    //整型函数过程,对stToken中的字符串查找保留字表,若它是一个保留字则返回它的编码,否则返回О值(假定0不是保留字的编码)。
    public int reserve(){
        Symbol symbol=new Symbol(strToken);
        if(keywordList.contains(symbol)){
            return keywordList.get(keywordList.indexOf(symbol)).getId();
        }
        return 0;
    }
    //子程序过程,将搜索指示器回调一个字符位置,将ch置为空白字符。
    public void retract(){
        position--;
        if(ch!=EOF){
            ch=' ';
        }
    }
    //整型函数过程,将strToken中的标识符插入符号表,返回符号表指针。
    public int insertId(String strToken){
        Symbol symbol=new Symbol(strToken);
        identifierList.add(symbol);
        return identifierList.indexOf(symbol);

    }
    //整型函数过程,将strToken中的常数插入常数表,返回常数表指针。
    public int insertConst(String strToken){
        Symbol symbol=new Symbol(strToken);
        constantList.add(symbol);
        return constantList.indexOf(symbol);
    }

    //根据图3.3的状态转移图实现
    public void analyzer(){
        int id,code;
        strToken="";
        getChar();
        getBC();
        if(ch==EOF){
            System.out.println("词法分析结束");
            return;
        }
        if(isLetter()){
            while ((isLetter()||isDigit())&&ch!=EOF){
                concat();
                getChar();
            }
            retract();
            id =reserve();
            //0是标识符 不是0就是保留字
            if(id ==0){
                code=insertId(strToken);
                Symbol symbol=keywordList.get(keywordList.indexOf(new Symbol("identifierList")));
                //<id,value,code> 编码 符号 索引
                int idId=symbol.getId();//标识符在关键字中的编码
                symbolList.add(new Symbol(idId,strToken,code));
            }else{
                int keyCode= keywordList.indexOf(new Symbol(id,strToken));//关键字的索引
                symbolList.add(new Symbol(id,strToken,keyCode));
            }
        }else if (isDigit()){
            while (isDigit()){
                concat();
                getChar();
            }
            retract();
            code=insertConst(strToken);
            id=keywordList.get(keywordList.indexOf(new Symbol("constantList"))).getId();
            symbolList.add(new Symbol(id,strToken,code));
        } else if (ch=='=') {
            code=keywordList.indexOf(new Symbol("="));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"=",code));
        }else if (ch=='+') {
            code=keywordList.indexOf(new Symbol("+"));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"+",code));
        }else if (ch=='*') {
            getChar();
            if(ch=='*'){
                code=keywordList.indexOf(new Symbol("**"));
                id=keywordList.get(code).getId();
                symbolList.add(new Symbol(id,"**",code));
            }else{
                retract();
                code=keywordList.indexOf(new Symbol("*"));
                id=keywordList.get(code).getId();
                symbolList.add(new Symbol(id,"*",code));
            }
        }else if (ch==',') {
            code=keywordList.indexOf(new Symbol(","));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,",",code));
        }else if (ch=='(') {
            code=keywordList.indexOf(new Symbol("("));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"(",code));
        }else if (ch==')') {
            code=keywordList.indexOf(new Symbol(")"));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,")",code));
        }else{
            procError();//出错处理
        }

    }
    private void procError() {
        throw new RuntimeException("词法分析出错"+"\n"+"出错位置为"+position);
    }
    public void analyzers(){
        while (ch!=EOF){
            analyzer();
        }
    }

    //测试关键词表的赋值
    private static void testKeywordList(){
        LexicalAnalyzer lex=new LexicalAnalyzer();
        for (Symbol s:lex.keywordList) {
            System.out.println(s);
        }
    }

    //测试词法分析器
    public void testAnalyzers(){
//        String input="DIM IF DO STOP END abc def 123 456 = + * ** , () DIM";
        String input=initInput();
        System.out.println("输出input串");
        System.out.println(input);
        LexicalAnalyzer lex=new LexicalAnalyzer(input);
        lex.analyzers();
        System.out.println("输出单词表");
        lex.toStringSymbolList();
        System.out.println("正在把单词表存入symbols.txt");
        lex.storeSymbols();
    }

    //初始化input
    public  String initInput(){
        File file = new File("input.txt");
        if (file.exists()) {
            System.out.println("input.txt文件存在");
            System.out.println("读取文件,input");
            return loadInput();
        } else {
            System.out.println("input.txt文件不存在");
            System.out.println("随机初始化input");
            return randomInput();
        }
    }


    //读入input.txt到input
    public String loadInput(){
        String input="";
        try {
            File file = new File("input.txt");
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            input = sb.toString();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return input;
    }



    //没有input.txt随机初始化input
    public String randomInput(){
        final String ERROR="~~";//错误字符

        Random rand=new Random();
        StringBuilder input= new StringBuilder();

        for(int i=0;i<10;i++){
            String value;
            int index=rand.nextInt(keywordList.size()+1);//故意+1 人为使其出现错误单词
            if(index>=keywordList.size()){
                value=ERROR;//错误单词
            }else{
                value=keywordList.get(index).getValue();
                if(value.equals("constantList")){
                    StringBuilder constant= new StringBuilder();
                    for(int j=0;j<3;j++){
                        constant.append(rand.nextInt(10));
                    }
                    value= constant.toString();
                }else if("identifierList".equals(value)){
                    StringBuilder letter= new StringBuilder();
                    for(int j=0;j<3;j++){
                        char c=(char)(rand.nextInt(26)+'a');
                        letter.append(c);
                    }
                    value= letter.toString();
                }
            }

            input.append(value).append(" ");
        }
        return input.toString();
    }



    //把symbolList中的数据写入symbols.txt
    public void storeSymbols() {
        String fileName="symbols.txt";
        storeList(fileName,symbolList);
    }

    //存储表到文件中
    public void storeList(String fileName,List<Symbol> list){
        FileWriter writer=null;
        try {
            writer = new FileWriter(fileName);
            for (Symbol symbol : list) {
                writer.write(symbol.toString() + "\n");
            }

            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        //测试
        //testKeywordList();

        //测试
        new LexicalAnalyzer().testAnalyzers();
    }

}

代码1结果

无keywords.txt

结果如下

keywords.txt文件不存在
采用默认的关键词表
input.txt文件存在
读取文件,input
输出input串
DIM IF DO STOP END abc def 123 456 = + * ** , ()
keywords.txt文件不存在
采用默认的关键词表
词法分析结束
输出单词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}
正在把单词表存入symbols.txt

Process finished with exit code 0

有keywords.txt文件

1  DIM
2  IF
3  DO
4  STOP
5  END
6  identifierList
7  constantList
8  =
9  +
10 *
11 **
12 ,
13 (
14 )

结果

和默认初始化一样

有input.txt文件时

正确情况

DIM IF DO STOP END abc def 123 456 = + * ** , ()

结果如下

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件存在
读取文件,input
输出input串
DIM IF DO STOP END abc def 123 456 = + * ** , ()
keywords.txt文件存在
读取文件,加载关键词表
词法分析结束
输出单词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}
正在把单词表存入symbols.txt

Process finished with exit code 0

错误情况(~是错误单词)

DIM IF DO STOP END abc def 123 456 = + * ** , () ~

结果如下

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件存在
读取文件,input
输出input串
DIM IF DO STOP END abc def 123 456 = + * ** , () ~
keywords.txt文件存在
读取文件,加载关键词表
Exception in thread "main" java.lang.RuntimeException: 词法分析出错
出错位置为50
	at s1.LexicalAnalyzer.procError(LexicalAnalyzer.java:242)
	at s1.LexicalAnalyzer.analyzer(LexicalAnalyzer.java:237)
	at s1.LexicalAnalyzer.analyzers(LexicalAnalyzer.java:246)
	at s1.LexicalAnalyzer.testAnalyzers(LexicalAnalyzer.java:265)
	at s1.LexicalAnalyzer.main(LexicalAnalyzer.java:379)

没有input.txt文件时

随机生成input单词串

正确情况

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件不存在
随机初始化input
输出input串
DIM IF * IF DIM ( , END ) IF 
keywords.txt文件存在
读取文件,加载关键词表
词法分析结束
输出单词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=10, value='*', code=9}
Symbol{id=2, value='IF', code=1}
Symbol{id=1, value='DIM', code=0}
Symbol{id=13, value='(', code=12}
Symbol{id=12, value=',', code=11}
Symbol{id=5, value='END', code=4}
Symbol{id=14, value=')', code=13}
Symbol{id=2, value='IF', code=1}
正在把单词表存入symbols.txt

错误情况

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件不存在
随机初始化input
输出input串
) ~~ + + DO 438 ~~ IF ** IF 
keywords.txt文件存在
读取文件,加载关键词表
Exception in thread "main" java.lang.RuntimeException: 词法分析出错
出错位置为3
	at s1.LexicalAnalyzer.procError(LexicalAnalyzer.java:242)
	at s1.LexicalAnalyzer.analyzer(LexicalAnalyzer.java:237)
	at s1.LexicalAnalyzer.analyzers(LexicalAnalyzer.java:246)
	at s1.LexicalAnalyzer.testAnalyzers(LexicalAnalyzer.java:265)
	at s1.LexicalAnalyzer.main(LexicalAnalyzer.java:379)

Process finished with exit code 1

symbols.txt

正确情况

Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}

错误情况

不写入

在此处修改,就可以写入了

抛出异常之前
进行输出单词表和写单词表

private void procError() {
        toStringSymbolList();
        storeSymbols();
        throw new RuntimeException("词法分析出错"+"\n"+"出错位置为"+position);
    }

结果如下

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件存在
读取文件,input
输出input串
DIM IF DO STOP END abc def 123 456 = + * ** , () ~
keywords.txt文件存在
读取文件,加载关键词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}
Exception in thread "main" java.lang.RuntimeException: 词法分析出错
出错位置为50
	at s1.LexicalAnalyzer.procError(LexicalAnalyzer.java:244)
	at s1.LexicalAnalyzer.analyzer(LexicalAnalyzer.java:237)
	at s1.LexicalAnalyzer.analyzers(LexicalAnalyzer.java:248)
	at s1.LexicalAnalyzer.testAnalyzers(LexicalAnalyzer.java:267)
	at s1.LexicalAnalyzer.main(LexicalAnalyzer.java:381)

Process finished with exit code 1

2023-4-2 18:05:18

代码2

2023-4-2 18:28:13

为了应对各种词法分析器,我们发现
除了关键字表,
就只有状态转移图不一样
也就是LexicalAnalyzer.analyzer()方法不一样

所以
我们用一个抽象父类AbstractLexicalAnalyzer来封装其他方法
让不同的子类实现来重写analyzer方法

另外
我们还可以在子类中把父类的出错处理重写了
使其跳过出错,继续分析

AbstractLexicalAnalyzer

package s1;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 词法分析器抽象类
 */
public abstract  class AbstractLexicalAnalyzer {
    public List<Symbol> keywordList=new ArrayList<>();//关键词表
    public List<Symbol> identifierList=new ArrayList<>();//变量名表
    public List<Symbol> constantList=new ArrayList<>();//常量表
    public List<Symbol> symbolList=new ArrayList<>();//运行中单词表 存放<种类,索引> 根据出现的顺序依次添加

    String input;//输入的单词串
    int position;//扫描指针

    protected AbstractLexicalAnalyzer() {
    }

    protected AbstractLexicalAnalyzer(String input) {
        this.input = input;
        this.position = 0;
    }

    public void toStringSymbolList(){
        for (Symbol s:symbolList) {
//            System.out.println(s.toStringIdCode());
            System.out.println(s.toString());
        }
    }

    //创建关键字表 根据表3.1实现
    {
        File file = new File("keywords.txt");
        if (file.exists()) {
            System.out.println("keywords.txt文件存在");
            System.out.println("读取文件,加载关键词表");
            loadKeywordList();
        } else {
            System.out.println("keywords.txt文件不存在");
            System.out.println("采用默认的关键词表");
            initKeywordList();
        }
    }

    //读取keywords.txt文件的内容加载到keywordList中
    public void loadKeywordList() {
        try {
            BufferedReader reader = new BufferedReader(new FileReader("keywords.txt"));
            String line;
            while ((line = reader.readLine()) != null) {
                String[] arr = line.split("\\s+");
                keywordList.add(new Symbol(Integer.parseInt(arr[0]), arr[1]));
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //默认的关键词表
    public void initKeywordList(){
        //保留字采用一词一类
        keywordList.add(new Symbol(1,"DIM"));
        keywordList.add(new Symbol(2,"IF"));
        keywordList.add(new Symbol(3,"DO"));
        keywordList.add(new Symbol(4,"STOP"));
        keywordList.add(new Symbol(5,"END"));
        //标识符 二次查询
        keywordList.add(new Symbol(6,"identifierList"));
        //常量表 二次查询
        keywordList.add(new Symbol(7,"constantList"));
        //算符采用一词一类
        keywordList.add(new Symbol(8,"="));
        keywordList.add(new Symbol(9,"+"));
        keywordList.add(new Symbol(10,"*"));
        keywordList.add(new Symbol(11,"**"));
        keywordList.add(new Symbol(12,","));
        keywordList.add(new Symbol(13,"("));
        keywordList.add(new Symbol(14,")"));
    }

    //给所有关键字符号赋内码值(也可以不赋值)
    {
        //取出keywordList的符号,给他赋值其索引
        for (Symbol s:keywordList) {
            s.setCode(keywordList.indexOf(s));
        }
    }


    //根据3,2,4编写

    //字符变量,存放最新读进的源程序字符
    static char ch;

    static final char EOF='#';

    // 字符数组,存放构成单词符号的字符串
    public String strToken;

    //子程序过程,将下一输入字符读到ch中,搜索指示器前移一字符位置。
    public  void getChar() {
        if (position<input.length()){
            ch=input.charAt(position++);
        }else{
            ch=EOF;
        }
    }


    //子程序过程,检查ch中的字符是否为空白。若是,则调用GetChar直至ch中进人一个非空白字符。
    public void getBC(){
        if(ch==' '){
            getChar();
            getBC();//递归
        }
    }

    //子程序过程,将ch中的字符连接到strToken之后。
    public void concat(){
        strToken=strToken.concat(""+ch);
    }

    //布尔函数过程,它们分别判断ch中的字符是否为字母和数字。
    public boolean isLetter(){
        return ch >= 'A' && ch <= 'z';
    }

    public boolean isDigit(){
        return ch >= '0' && ch <= '9';
    }

    //整型函数过程,对stToken中的字符串查找保留字表,若它是一个保留字则返回它的编码,否则返回О值(假定0不是保留字的编码)。
    public int reserve(){
        Symbol symbol=new Symbol(strToken);
        if(keywordList.contains(symbol)){
            return keywordList.get(keywordList.indexOf(symbol)).getId();
        }
        return 0;
    }
    //子程序过程,将搜索指示器回调一个字符位置,将ch置为空白字符。
    public void retract(){
        position--;
        if(ch!=EOF){
            ch=' ';
        }
    }
    //整型函数过程,将strToken中的标识符插入符号表,返回符号表指针。
    public int insertId(String strToken){
        Symbol symbol=new Symbol(strToken);
        identifierList.add(symbol);
        return identifierList.indexOf(symbol);

    }
    //整型函数过程,将strToken中的常数插入常数表,返回常数表指针。
    public int insertConst(String strToken){
        Symbol symbol=new Symbol(strToken);
        constantList.add(symbol);
        return constantList.indexOf(symbol);
    }

    //状态转移图由子类实现
    public abstract void analyzer();

    //处理错误
    //父类抛出异常处理错误
    void procError() {
        toStringSymbolList();
        storeSymbols();
        throw new RuntimeException("词法分析出错"+"\n"+"出错位置为"+position);
    }

    public void analyzers(){
        while (ch!=EOF){
            analyzer();
        }
    }


    //初始化input
    public  String initInput(){
        File file = new File("input.txt");
        if (file.exists()) {
            System.out.println("input.txt文件存在");
            System.out.println("读取文件,input");
            return loadInput();
        } else {
            System.out.println("input.txt文件不存在");
            System.out.println("随机初始化input");
            return randomInput();
        }
    }


    //读入input.txt到input
    public String loadInput(){
        String input="";
        try {
            File file = new File("input.txt");
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            input = sb.toString();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return input;
    }


    //没有input.txt随机初始化input
    public String randomInput(){
        final String ERROR="~~";//错误字符

        Random rand=new Random();
        StringBuilder input= new StringBuilder();

        for(int i=0;i<10;i++){
            String value;
            int index=rand.nextInt(keywordList.size()+1);//故意+1 人为使其出现错误单词
            if(index>=keywordList.size()){
                value=ERROR;//错误单词
            }else{
                value=keywordList.get(index).getValue();
                if(value.equals("constantList")){
                    StringBuilder constant= new StringBuilder();
                    for(int j=0;j<3;j++){
                        constant.append(rand.nextInt(10));
                    }
                    value= constant.toString();
                }else if("identifierList".equals(value)){
                    StringBuilder letter= new StringBuilder();
                    for(int j=0;j<3;j++){
                        char c=(char)(rand.nextInt(26)+'a');
                        letter.append(c);
                    }
                    value= letter.toString();
                }
            }

            input.append(value).append(" ");
        }
        return input.toString();
    }



    //把symbolList中的数据写入symbols.txt
    public void storeSymbols() {
        String fileName="symbols.txt";
        storeList(fileName,symbolList);
    }

    //存储表到文件中
    public void storeList(String fileName,List<Symbol> list){
        FileWriter writer=null;
        try {
            writer = new FileWriter(fileName);
            for (Symbol symbol : list) {
                writer.write(symbol.toString() + "\n");
            }

            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }



}

LexicalAnalyzer

package s1;


/**
 * 词法分析器类
 */
public class LexicalAnalyzer extends AbstractLexicalAnalyzer{

    public LexicalAnalyzer() {

    }
    public LexicalAnalyzer(String input) {
        super(input);
    }


    //根据图3.3的状态转移图实现
    @Override
    public void analyzer(){
        int id,code;
        strToken="";
        getChar();
        getBC();
        if(ch==EOF){
            System.out.println("词法分析结束");
            return;
        }
        if(isLetter()){
            while ((isLetter()||isDigit())&&ch!=EOF){
                concat();
                getChar();
            }
            retract();
            id =reserve();
            //0是标识符 不是0就是保留字
            if(id ==0){
                code=insertId(strToken);
                Symbol symbol=keywordList.get(keywordList.indexOf(new Symbol("identifierList")));
                //<id,value,code> 编码 符号 索引
                int idId=symbol.getId();//标识符在关键字中的编码
                symbolList.add(new Symbol(idId,strToken,code));
            }else{
                int keyCode= keywordList.indexOf(new Symbol(id,strToken));//关键字的索引
                symbolList.add(new Symbol(id,strToken,keyCode));
            }
        }else if (isDigit()){
            while (isDigit()){
                concat();
                getChar();
            }
            retract();
            code=insertConst(strToken);
            id=keywordList.get(keywordList.indexOf(new Symbol("constantList"))).getId();
            symbolList.add(new Symbol(id,strToken,code));
        } else if (ch=='=') {
            code=keywordList.indexOf(new Symbol("="));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"=",code));
        }else if (ch=='+') {
            code=keywordList.indexOf(new Symbol("+"));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"+",code));
        }else if (ch=='*') {
            getChar();
            if(ch=='*'){
                code=keywordList.indexOf(new Symbol("**"));
                id=keywordList.get(code).getId();
                symbolList.add(new Symbol(id,"**",code));
            }else{
                retract();
                code=keywordList.indexOf(new Symbol("*"));
                id=keywordList.get(code).getId();
                symbolList.add(new Symbol(id,"*",code));
            }
        }else if (ch==',') {
            code=keywordList.indexOf(new Symbol(","));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,",",code));
        }else if (ch=='(') {
            code=keywordList.indexOf(new Symbol("("));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,"(",code));
        }else if (ch==')') {
            code=keywordList.indexOf(new Symbol(")"));
            id=keywordList.get(code).getId();
            symbolList.add(new Symbol(id,")",code));
        }else{
            procError();//出错处理
        }

    }

    //重写出错处理,跳过1下,继续分析
   @Override
    public void procError(){
        System.out.println("词法分析出错"+"\n"+"出错位置为"+position);
        System.out.println("跳过错误,继续分析");
        position++;
        if(ch!=EOF){
            ch=' ';
        }
    }

    //测试关键词表的赋值
    private static void testKeywordList(){
        LexicalAnalyzer lex=new LexicalAnalyzer();
        for (Symbol s:lex.keywordList) {
            System.out.println(s);
        }
    }

    //测试词法分析器
    private void testAnalyzers(){
//        String input="DIM IF DO STOP END abc def 123 456 = + * ** , () DIM";
        String input=initInput();
        System.out.println("输出input串");
        System.out.println(input);
        LexicalAnalyzer lex=new LexicalAnalyzer(input);
        lex.analyzers();
        System.out.println("输出单词表");
        lex.toStringSymbolList();
        System.out.println("正在把单词表存入symbols.txt");
        lex.storeSymbols();
    }


    public static void main(String[] args) {
        //测试
        //testKeywordList();

        //测试
        new LexicalAnalyzer().testAnalyzers();
    }

}

代码2结果

input.txt

一处错误

~ DIM IF DO STOP END abc def 123 456 = + * ** , ()

结果如下

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件存在
读取文件,input
输出input串
~ DIM IF DO STOP END abc def 123 456 = + * ** , ()
keywords.txt文件存在
读取文件,加载关键词表
词法分析出错
出错位置为1
跳过错误,继续分析
词法分析结束
输出单词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}
正在把单词表存入symbols.txt

Process finished with exit code 0

多处错误

~ DIM IF DO STOP END abc ~ def 123 456 = + ~ * ** , () ~

结果如下

keywords.txt文件存在
读取文件,加载关键词表
input.txt文件存在
读取文件,input
输出input串
~ DIM IF DO STOP END abc ~ def 123 456 = + ~ * ** , () ~
keywords.txt文件存在
读取文件,加载关键词表
词法分析出错
出错位置为1
跳过错误,继续分析
词法分析出错
出错位置为26
跳过错误,继续分析
词法分析出错
出错位置为44
跳过错误,继续分析
词法分析出错
出错位置为56
跳过错误,继续分析
词法分析结束
输出单词表
Symbol{id=1, value='DIM', code=0}
Symbol{id=2, value='IF', code=1}
Symbol{id=3, value='DO', code=2}
Symbol{id=4, value='STOP', code=3}
Symbol{id=5, value='END', code=4}
Symbol{id=6, value='abc', code=0}
Symbol{id=6, value='def', code=1}
Symbol{id=7, value='123', code=0}
Symbol{id=7, value='456', code=1}
Symbol{id=8, value='=', code=7}
Symbol{id=9, value='+', code=8}
Symbol{id=10, value='*', code=9}
Symbol{id=11, value='**', code=10}
Symbol{id=12, value=',', code=11}
Symbol{id=13, value='(', code=12}
Symbol{id=14, value=')', code=13}
正在把单词表存入symbols.txt

Process finished with exit code 0

2023-4-2 19:01:37

高级代码

要求:代码的高级功能

更多的关键字(运算符)
需要编写keywords.txt

更多的常数(科学计数法 浮点数 字符串常量)
需要重写analyzer

更多的功能(过滤无效字符、数值转换、宏展开、预包含处理)
需要重写analyzer

说明
在关键词表中

6  identifierList
7  constantList

要来做

标识符(id=6,value=identifierList)
常量表(id=7,value=constantList)

没有什么意义

只在随机生成input中用来生成标识符和常量

也可以没有
这样的话,符号表的输出就是

标识符(id=0,value=具体标识符的值)
常量表(id=0,value=具体变量的值)

仅在id有所区别,其余没变

如果需要修改的话
analyzer()中标识符和常量表的id设置为0,就行
不再需要在keywordList中查找

还有
出错位置没有行数
需要修改loadInput()逻辑
使其每读入一行,就进行语法分析处理
并且需要row行数属性来配合

另外需要注意:
代码中EOF定义为#
ch=EOF 作为input结束的标志
如果#号是有意义的符号
就需要重写定义static final char EOF='#';
例如:static final char EOF= (char) 128;

请看:
实验一 词法分析器+【编译原理】

最后

2023-4-2 19:01:40

祝大家逢考必过
点赞收藏关注哦