Java 實現《編譯原理》簡單詞法分析功能

肖朋偉發表於2019-06-13

Java 實現《編譯原理》簡單詞法分析功能

簡易詞法分析功能

要求及功能

(1)讀取一個 txt 程式檔案(最後的 # 作為結束標誌,不可省去)

{
  int a, b;
  a = 10;
  if(a>=1){
    b = a + 20;
  }
}

(2)詞法識別分析表
單詞類別|單詞自身值|內部編碼
-|-|-
關鍵字| int、for、while、do、return、break、continue| 1
識別符號| 除關鍵字外的以字母開頭,後跟字母、數字的字元序列| 2
常數| 無符號整型數| 3
運算子| +、-、*、/、>、<、=、>=、<=、!=| 4
界限符| ,、;、{、}、(、)| 5
換行符|\n| 6

(3)輸出結果:

(5,{)
(6,\n)
(1,int)
(2,a)
(5,,)
(2,b)
(5,;)
(6,\n)
(2,a)
(4,=)
(3,10)
(5,;)
(6,\n)
(2,if)
(5,()
(2,a)
(4,>=)
(3,1)
(5,))
(5,{)
(6,\n)
(2,b)
(4,=)
(2,a)
(4,+)
(3,20)
(5,;)
(6,\n)
(5,})
(6,\n)
(5,})
(6,\n)
(0,#)

並儲存成新的 txt 檔案

程式設計實現

(1)程式檔案目錄:

Java 實現《編譯原理》簡單詞法分析功能

(2)Word.java 檔案:

package com.java997.analyzer.lexical;

/**
 * <p>
 * 表示識別後的詞實體類
 *
 * @author XiaoPengwei
 * @since 2019-06-13
 */
public class Word {

    /**
     * 種別碼
     */
    private int typeNum;

    /**
     * 掃描得到的詞
     */
    private String word;

    public int getTypeNum() {
        return typeNum;
    }

    public void setTypeNum(int typeNum) {
        this.typeNum = typeNum;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}

(3)CodeScanner.java 檔案:

package com.java997.analyzer.lexical;

/**
 * <p>
 * 字元掃描
 *
 * @author XiaoPengwei
 * @since 2019-06-13
 */
public class CodeScanner {

    private static String _KEY_WORD_END = "end string of string";
    private int charNum = 0;
    private Word word;

    private char[] input = new char[255];
    private char[] token = new char[255];
    private int p_input = 0;
    private int p_token = 0;

    private char ch;

    /**
     * 關鍵字陣列
     */
    private String[] rwtab = {"int", "if", "while", "do", "return", "break", "continue", _KEY_WORD_END};

    /**
     * 邏輯運算陣列
     */
    private String[] logicTab = {"==",">=","<=","!=", _KEY_WORD_END};

    public CodeScanner(char[] input) {
        this.input = input;
    }

    /**
     * 取下一個字元
     *
     * @return
     */
    public char m_getch() {
        if (p_input < input.length) {
            ch = input[p_input];
            p_input++;
        }
        return ch;
    }

    /**
     * 如果是識別符號或者空白符就取下一個字元
     */
    public void getbc() {
        while ((ch == ' ' || ch == '\t') && p_input < input.length) {
            ch = input[p_input];
            p_input++;
        }
    }

    /**
     * 把當前字元和原有字串連線
     */
    public void concat() {
        token[p_token] = ch;
        p_token++;
        token[p_token] = '\0';
    }

    /**
     * 回退一個字元
     */
    public void retract() {
        p_input--;
    }

    /**
     * 判斷是否為字母
     *
     * @return boolean
     * @author XiaoPengwei
     */
    public boolean isLetter() {
        return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z';
    }

    /**
     * 判斷是否為數字
     *
     * @return boolean
     * @author XiaoPengwei
     */
    public boolean isDigit() {
        return ch >= '0' && ch <= '9';
    }

    /**
     * 檢視 token 中的字串是否是關鍵字,是的話返回關鍵字種別編碼,否則返回 2
     *
     * @return
     */
    public int isKey() {
        int i = 0;
        while (rwtab[i].compareTo(_KEY_WORD_END) != 0) {
            if (rwtab[i].compareTo(new String(token).trim()) == 0) {
                return i + 1;
            }
            i++;
        }
        return 2;
    }

    /**
     * 可能是邏輯預算字元
     *
     * @return
     */
    public Boolean isLogicChar() {
        return ch == '>' || ch == '<'|| ch == '='|| ch == '!';
    }


    /**
     * 檢視 token 中的字串是否是邏輯運算子,是的話返回關鍵字種別編碼,否則返回 2
     *
     * @return
     */
    public int isLogicTab() {
        int i = 0;
        while (logicTab[i].compareTo(_KEY_WORD_END) != 0) {
            if (logicTab[i].compareTo(new String(token).trim()) == 0) {
                return i + 1;
            }
            i++;
        }
        return 4;
    }

    /**
     * 能夠識別換行,單行註釋和多行註釋的
     * 換行的種別碼設定成30
     * 多行註釋的種別碼設定成31
     *
     * @return
     */
    public Word scan() {
        token = new char[255];
        Word myWord = new Word();
        myWord.setTypeNum(10);
        myWord.setWord("");

        p_token = 0;
        m_getch();
        getbc();
        if (isLetter()) {
            while (isLetter() || isDigit()) {
                concat();
                m_getch();
            }
            retract();
            myWord.setTypeNum(isKey());
            myWord.setWord(new String(token).trim());
            return myWord;
        } else if (isLogicChar()) {
            while (isLogicChar()) {
                concat();
                m_getch();
            }
            retract();
            myWord.setTypeNum(4);
            myWord.setWord(new String(token).trim());
            return myWord;
        } else if (isDigit()) {
            while (isDigit()) {
                concat();
                m_getch();
            }
            retract();
            myWord.setTypeNum(3);
            myWord.setWord(new String(token).trim());
            return myWord;
        } else {
            switch (ch) {
                //5
                case ',':
                    myWord.setTypeNum(5);
                    myWord.setWord(",");
                    return myWord;
                case ';':
                    myWord.setTypeNum(5);
                    myWord.setWord(";");
                    return myWord;
                case '{':
                    myWord.setTypeNum(5);
                    myWord.setWord("{");
                    return myWord;
                case '}':
                    myWord.setTypeNum(5);
                    myWord.setWord("}");
                    return myWord;
                case '(':
                    myWord.setTypeNum(5);
                    myWord.setWord("(");
                    return myWord;
                case ')':
                    myWord.setTypeNum(5);
                    myWord.setWord(")");
                    return myWord;
                //4
                case '=':
                    myWord.setTypeNum(4);
                    myWord.setWord("=");
                    return myWord;
                case '+':
                    myWord.setTypeNum(4);
                    myWord.setWord("+");
                    return myWord;
                case '-':
                    myWord.setTypeNum(4);
                    myWord.setWord("-");
                    return myWord;
                case '*':
                    myWord.setTypeNum(4);
                    myWord.setWord("*");
                    return myWord;
                case '/':
                    myWord.setTypeNum(4);
                    myWord.setWord("/");
                    return myWord;

                case '\n':
                    myWord.setTypeNum(6);
                    myWord.setWord("\\n");
                    return myWord;
                case '#':
                    myWord.setTypeNum(0);
                    myWord.setWord("#");
                    return myWord;
                default:
                    concat();
                    myWord.setTypeNum(-1);
                    myWord.setWord("ERROR INFO: WORD = \"" + new String(token).trim() + "\"");
                    return myWord;
            }
        }
    }
}

(4)MainAnalyzer.java 檔案:

package com.java997.analyzer.lexical;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Scanner;

/**
 * <p>
 * 執行主程式
 *
 * @author XiaoPengwei
 * @since 2019-06-13
 */
public class MainAnalyzer {
    private File inputFile;
    private File outputFile;
    private String fileContent;
    private ArrayList<Word> list = new ArrayList<>();

    /**
     * 構造方法
     *
     * @param input
     * @param output
     * @author XiaoPengwei
     */
    public MainAnalyzer(String input, String output) {
        //例項化輸入檔案
        inputFile = new File(input);

        //例項化輸出檔案
        outputFile = new File(output);
    }

    /**
     * 從指定的 txt 檔案中讀取源程式檔案內容
     *
     * @return java.lang.String
     */
    public String getContent() {
        StringBuilder stringBuilder = new StringBuilder();
        try (Scanner reader = new Scanner(inputFile)) {
            while (reader.hasNextLine()) {
                String line = reader.nextLine();
                stringBuilder.append(line + "\n");
                System.out.println(line);
            }
            System.out.println("Successful reading of files:" + inputFile.getName());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return fileContent = stringBuilder.toString();
    }

    /**
     * 然後掃描程式,在程式結束前將掃描到的詞新增到 list 中
     * 最後把掃描結果儲存到指定的檔案中
     *
     * @param fileContent
     * @return void
     */
    public void analyze(String fileContent) {
        int over = 1;
        Word word = new Word();

        //呼叫掃描程式
        CodeScanner scanner = new CodeScanner(fileContent.toCharArray());
        System.out.println("The result:");
        while (over != 0) {
            word = scanner.scan();
            System.out.println("(" + word.getTypeNum() + "," + word.getWord() + ")");
            list.add(word);
            over = word.getTypeNum();
        }
        saveResult();
    }

    /**
     * 將結果寫入到到指定檔案中
     * 如果檔案不存在,則建立一個新的檔案
     * 用一個 foreach 迴圈將 list 中的項變成字串寫入到檔案中
     */
    public void saveResult() {

        //建立檔案
        if (!outputFile.exists()) {
            try {
                outputFile.createNewFile();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        //寫入檔案
        try (Writer writer = new FileWriter(outputFile)) {
            for (Word word : list) {
                writer.write("(" + word.getTypeNum() + " ," + word.getWord() + ")\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        //注意輸入檔案路徑/名稱必須對, 輸出檔案可以由程式建立
        MainAnalyzer analyzer = new MainAnalyzer("D:\\analyzer\\src\\main\\java\\com\\java997\\analyzer\\lexical\\input.txt", "D:\\analyzer\\src\\main\\java\\com\\java997\\analyzer\\lexical\\output.txt");

        analyzer.analyze(analyzer.getContent());
    }
}

(5)input.txt 檔案:

{
  int a, b;
  a = 10;
  if(a>=1){
    b = a + 20;
  }
}
#

執行測試

Java 實現《編譯原理》簡單詞法分析功能

相關文章