一个简单的词法分析器(带界面)

Java实现简单的词法分析器:

  • 需求分析

    • 5类符号

      • 保留字(keywords):if、int、for、while、do、return、break、continue等等;单词种别码为1
      • 其他的字符都为标识符(identifier);单词种别码为2
      • 常数为无符号数(unsigned number);单词种别码为3
      • 运算符(operator)包括:+、-、*、/、=、>、<等;单词种别码为4。
      • 分隔符(separator)包括: “,”“;”“(”“)”“{”“}”等; 单词种别码为5。
    • 分析流程

流程图
  • 代码设计

    • 目录结构

目录结构

symbols目录下的txt文件分别保存关键字信息(keywords.txt)、操作符信息(operators.txt)、分隔符信息(separators.txt)。
Util包中分别为文件读取工具类(FileReadUtil)和词法分析工具类(LexerUtil)
symbols包中为符号类
lexer包中的类和Main结合用来控制界面显示,界面使用JavaFx实现。

  • 符号类设计

    • 类图
  • 所有的符号类都继承自抽象类TokenToken类中带参构造函数用来初始化,读取保存在txt文件中的保留字,标识符,分隔符信息并保存在List中。

  • KeyWord类、Operator类、Separator类重写这个带参构造函数来读取符号并调用TokengetList()方法将读取的的符号保存到它们的内部的静态List中。

  • Identifier类、UnsignedNumber类不重写构造函数,即他们只有默认的无参构造函数。

这部分只给出TokenKeyWordIdentifier的代码实现。
完整代码在我的Github: https://github.com/LiuJinxuan/Lexer.git

Token类

package com.company.symbols;
import com.company.utils.FileReadUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by Ljx on 2017/5/6.
*/
public abstract class Token {
    private static List<String> tokens = new ArrayList<>();
    public Token() {
    }
    public Token(String fileName) {
        try {
            tokens = FileReadUtil.ReadSymbols(fileName);
        } catch (FileNotFoundException e) {
            System.out.println(fileName + " NOT FOUND!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    protected List<String> getTokens() {
        return tokens;
    }
    //返回识别码TAG
    public abstract int getTAG();
    //返回具体的类型
    public abstract String getDetail();
}

KeyWord类

package com.company.symbols;

import java.util.List;


/**
 * Created by Ljx on 2017/5/5.
 */
public class KeyWord extends Token {
    private static List<String> keyWords;
    private static int TAG = 1;

    public KeyWord(String fileName) {
        super(fileName);
        keyWords = super.getTokens();
    }

    public List<String> getKeywordList() {
        return keyWords;
    }

    @Override
    public int getTAG() {
        return TAG;
    }

    @Override
    public String getDetail() {
        return "保留字";
    }
}

Identifier类

package com.company.symbols;

/**
 * Created by Ljx on 2017/5/5.
 */
public class Identifier extends Token {
    private static int TAG = 2;

    @Override
    public int getTAG() {
        return TAG;
    }

    @Override
    public String getDetail() {
        return "标识符";
    }

}
  • 工具类

    • 文件读取工具类FileReadUtil

package com.company.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Ljx on 2017/5/5.
 */
public class FileReadUtil {
    //一行行读取文件,将每一行存到到List中
    public static List<String> ReadSymbols(String fileName) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader(fileName));
        String s;
        List<String> list = new ArrayList<>();
        while ((s = in.readLine()) != null) list.add(s);
        in.close();
        return list;
    }

    //得到添加行号后的代码,用于显示在界面上
    public static String GetCode(File file) throws IOException {
        int currentLine = 1;
        BufferedReader in = new BufferedReader(new FileReader(file));
        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = in.readLine()) != null) {
            sb.append(currentLine < 10 ? currentLine + "  |  " : currentLine + " |  ");  //对齐行号
            sb.append(s);
            sb.append("\n");
            currentLine++;
        }
        in.close();
        return sb.toString();
    }
}
  • 词法分析工具类LexerUtil

package com.company.utils;

import com.company.lexer.LexResult;
import com.company.symbols.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Ljx on 2017/5/5.
 *
 * 词法分析工具类
 *
 */
public class LexerUtil {
    private final static String KEYWORD_PATH = "symbols\\keywords.txt";
    private final static String OPERATOR_PATH = "symbols\\operators.txt";
    private final static String SEPARATOR_PATH = "symbols\\separators.txt";
    private final static KeyWord keyWord = new KeyWord(KEYWORD_PATH);
    private final static Operator operator = new Operator(OPERATOR_PATH);
    private final static Separator separator = new Separator(SEPARATOR_PATH);
    private final static UnsignedNumber unsignedNumber = new UnsignedNumber();
    private final static Identifier identifier = new Identifier();

    public static List<LexResult> LexicalAnalysis(File file) {
        int currentLine = 1;//用于保存该字符的行号
        List<LexResult> lexResultList = new ArrayList<>();
        try {
            BufferedReader in = new BufferedReader(new FileReader(file));
            String s;
            List<String> list;
            while ((s = in.readLine()) != null) {
                list = division(s); //分割字符
                for (String symbol : list) {
                    //用于判断当前字符属于哪种符号
                    Token currentType;
                    if      (isKeyWord(symbol))     currentType = keyWord;
                    else if (isOperator(symbol))    currentType = operator;
                    else if (isSeparator(symbol))   currentType = separator;
                    //若不是关键字,操作符,分隔符,将该字符强转为Integer类型
                    //抛出异常,表明该字符为标识符
                    //不抛出异常,表面该字符为无符号数
                    else {
                        try {
                            Integer.valueOf(symbol);
                            currentType = unsignedNumber;
                        } catch (NumberFormatException e) {
                            currentType = identifier;
                        }
                    }
                    LexResult lexResult = new LexResult(String.valueOf(currentLine),
                            symbol, String.valueOf(currentType.getTAG()), currentType.getDetail());
                    lexResultList.add(lexResult);
                }
                currentLine++;
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return lexResultList;
    }

    /**
     * 1.将字符串转化为字符数组,定义一个StringBuilder对象用来保存符号
     * 2.遍历字符数组,进行如下操作:
     *      if(字符 == 操作符 || 分隔符 || 空格) --> 从该字符分割
     *          if(StringBuilder长度不为0)-->将StringBuilder取出所有空格后存入List
     *          if(字符 == 空格) --> 将空格存入List
     *          清空StringBuilder;
     *          continue;
     *      else --> StringBuilder后添加该字符
     *      
     * @param s 一行语句
     * @return 一行行中分割后的字符列表
     */
    private static List<String> division(String s) {
        char[] chars = s.trim().toCharArray();  //去除首尾空格并转化为字符数组
        List<String> list = new ArrayList<>();  //保存组合出的单词和字符
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
            if (isOperator(String.valueOf(chars[i]))
                    || isSeparator(String.valueOf(chars[i]))
                    || chars[i] == ' ') {
                if (sb.length() != 0) list.add(sb.toString().replaceAll(" ", ""));
                if (chars[i] != ' ') list.add(String.valueOf(chars[i]));
                sb.delete(0, sb.length());  //清空StringBuilder
                continue;
            }
            sb.append(chars[i]);
        }
        return list;
    }

    private static boolean isKeyWord(String s) {
        return keyWord.getKeywordList().contains(s);
    }
    private static boolean isOperator(String s) {
        return operator.getOperatorList().contains(s);
    }
    private static boolean isSeparator(String s) {
        return separator.getSeparatorList().contains(s);
    }
}
  • 词法分析结果类LexResult

package com.company.lexer;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * Created by Ljx on 2017/5/7.
 */
public class LexResult {
    private final StringProperty line;
    private final StringProperty symbol;
    private final StringProperty TAG;
    private final StringProperty detail;

    public LexResult() {
        this(null, null, null, null);
    }

    public LexResult(String line, String symbol, String TAG, String detail) {
        this.line = new SimpleStringProperty(line);
        this.symbol = new SimpleStringProperty(symbol);
        this.TAG = new SimpleStringProperty(TAG);
        this.detail = new SimpleStringProperty(detail);
    }

    public String getLine() {
        return line.get();
    }

    public StringProperty lineProperty() {
        return line;
    }

    public void setLine(String line) {
        this.line.set(line);
    }

    public String getSymbol() {
        return symbol.get();
    }

    public StringProperty symbolProperty() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol.set(symbol);
    }

    public String getTAG() {
        return TAG.get();
    }

    public StringProperty TAGProperty() {
        return TAG;
    }

    public void setTAG(String TAG) {
        this.TAG.set(TAG);
    }

    public String getDetail() {
        return detail.get();
    }

    public StringProperty detailProperty() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail.set(detail);
    }

    @Override
    public String toString() {
        return "LexResult{" +
                "line=" + line +
                ", symbol=" + symbol +
                ", TAG=" + TAG +
                ", detail=" + detail +
                '}';
    }
}
初始界面

文件读取

分析结果

以上就是一个简单的词法分析器实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容