利用Java实现简单的词法分析器实例代码

首先看下我们要分析的代码段如下:

输出结果如下:


输出结果(a).PNG

输出结果(b).PNG

输出结果(c).PNG

括号里是一个二元式:(单词类别编码,单词位置编号)

代码如下:

package Yue.LexicalAnalyzer;

import java.io.*;

/*
 * 主程序
 */
public class Main {
  public static void main(String[] args) throws IOException {
    Lexer lexer = new Lexer();
    lexer.printToken();
    lexer.printSymbolsTable();
  }
}
package Yue.LexicalAnalyzer;

import java.io.*;
import java.util.*;

/*
 * 词法分析并输出
 */
public class Lexer {
  /*记录行号*/
  public static int line = 1;
  /*存放最新读入的字符*/
  char character = ' ';

  /*保留字*/
  Hashtable<String, KeyWord> keywords = new Hashtable<String, KeyWord>();
  /*token序列*/
  private ArrayList<Token> tokens = new ArrayList<Token>();
  /*符号表*/
  private ArrayList<Symbol> symtable = new ArrayList<Symbol>();

  /*读取文件变量*/
  BufferedReader reader = null;
  /*保存当前是否读取到了文件的结尾*/
  private Boolean isEnd = false;

  /* 是否读取到文件的结尾 */
  public Boolean getReaderState() {
    return this.isEnd;
  }

  /*打印tokens序列*/
  public void printToken() throws IOException {
    FileWriter writer = new FileWriter("E:\\lex.txt");
    System.out.println("词法分析结果如下:");
    System.out.print("杜悦-2015220201031\r\n\n");
    writer.write("杜悦-2015220201031\r\n\r\n");
    while (getReaderState() == false) {
      Token tok = scan();
      String str = "line " + tok.line + "\t(" + tok.tag + "," + tok.pos + ")\t\t"
          + tok.name + ": " + tok.toString() + "\r\n";
      writer.write(str);
      System.out.print(str);
    }
    writer.flush();

  }

  /*打印符号表*/
  public void printSymbolsTable() throws IOException {
    FileWriter writer = new FileWriter("E:\\symtab1.txt");
    System.out.print("\r\n\r\n符号表\r\n");
    System.out.print("编号\t行号\t名称\r\n");
    writer.write("符号表\r\n");
    writer.write("编号 " + "\t行号 " + "\t名称 \r\n");
    Iterator<Symbol> e = symtable.iterator();
    while (e.hasNext()) {
      Symbol symbol = e.next();
      String desc = symbol.pos + "\t" + symbol.line + "\t" + symbol.toString();
      System.out.print(desc + "\r\n");
      writer.write(desc + "\r\n");
    }

    writer.flush();
  }

  /*打印错误*/
  public void printError(Token tok) throws IOException{
    FileWriter writer = new FileWriter("E:\\error.txt");
    System.out.print("\r\n\r\n错误词法如下:\r\n");
    writer.write("错误词法如下:\r\n");
    String str = "line " + tok.line + "\t(" + tok.tag + "," + tok.pos + ")\t\t"
        + tok.name + ": " + tok.toString() + "\r\n";
    writer.write(str);
  }

  /*添加保留字*/
  void reserve(KeyWord w) {
    keywords.put(w.lexme, w);
  }

  public Lexer() {
    /*初始化读取文件变量*/
    try {
      reader = new BufferedReader(new FileReader("E:\\输入.txt"));
    } catch (IOException e) {
      System.out.print(e);
    }

    /*添加保留字*/
    this.reserve(KeyWord.begin);
    this.reserve(KeyWord.end);
    this.reserve(KeyWord.integer);
    this.reserve(KeyWord.function);
    this.reserve(KeyWord.read);
    this.reserve(KeyWord.write);
    this.reserve(KeyWord.aIf);
    this.reserve(KeyWord.aThen);
    this.reserve(KeyWord.aElse);
  }

  /*按字符读*/
  public void readch() throws IOException {
    character = (char) reader.read();
    if ((int) character == 0xffff) {
      this.isEnd = true;
    }
  }

  /*判断是否匹配*/
  public Boolean readch(char ch) throws IOException {
    readch();
    if (this.character != ch) {
      return false;
    }

    this.character = ' ';
    return true;
  }

  /*数字的识别*/
  public Boolean isDigit() throws IOException {
    if (Character.isDigit(character)) {
      int value = 0;
      while (Character.isDigit(character)) {
        value = 10 * value + Character.digit(character, 10);
        readch();
      }

      Num n = new Num(value);
      n.line = line;
      tokens.add(n);
      return true;
    } else
      return false;
  }

  /*保留字、标识符的识别*/
  public Boolean isLetter() throws IOException {
    if (Character.isLetter(character)) {
      StringBuffer sb = new StringBuffer();

      /*首先得到整个的一个分割*/
      while (Character.isLetterOrDigit(character)) {
        sb.append(character);
        readch();
      }

      /*判断是保留字还是标识符*/
      String s = sb.toString();
      KeyWord w = keywords.get(s);

      /*如果是保留字的话,w不应该是空的*/
      if (w != null) {
        w.line = line;
        tokens.add(w);
      } else {
        /*否则就是标识符,此处多出记录标识符编号的语句*/
        Symbol sy = new Symbol(s);
        Symbol mark = sy;      //用于标记已存在标识符
        Boolean isRepeat = false;
        sy.line = line;
        for (Symbol i : symtable) {
          if (sy.toString().equals(i.toString())) {
            mark = i;
            isRepeat = true;
          }
        }
        if (!isRepeat) {
          sy.pos = symtable.size() + 1;
          symtable.add(sy);
        } else if (isRepeat) {
          sy.pos = mark.pos;
        }
        tokens.add(sy);
      }
      return true;
    } else
      return false;
  }

  /*符号的识别*/
  public Boolean isSign() throws IOException {
    switch (character) {
      case '#':
        readch();
        AllEnd.allEnd.line = line;
        tokens.add(AllEnd.allEnd);
        return true;
      case '\r':
        if (readch('\n')) {
          readch();
          LineEnd.lineEnd.line = line;
          tokens.add(LineEnd.lineEnd);
          line++;
          return true;
        }
      case '(':
        readch();
        Delimiter.lpar.line = line;
        tokens.add(Delimiter.lpar);
        return true;
      case ')':
        readch();
        Delimiter.rpar.line = line;
        tokens.add(Delimiter.rpar);
        return true;
      case ';':
        readch();
        Delimiter.sem.line = line;
        tokens.add(Delimiter.sem);
        return true;
      case '+':
        readch();
        CalcWord.add.line = line;
        tokens.add(CalcWord.add);
        return true;
      case '-':
        readch();
        CalcWord.sub.line = line;
        tokens.add(CalcWord.sub);
        return true;
      case '*':
        readch();
        CalcWord.mul.line = line;
        tokens.add(CalcWord.mul);
        return true;
      case '/':
        readch();
        CalcWord.div.line = line;
        tokens.add(CalcWord.div);
        return true;
      case ':':
        if (readch('=')) {
          readch();
          CalcWord.assign.line = line;
          tokens.add(CalcWord.assign);
          return true;
        }
        break;
      case '>':
        if (readch('=')) {
          readch();
          CalcWord.ge.line = line;
          tokens.add(CalcWord.ge);
          return true;
        }
        break;
      case '<':
        if (readch('=')) {
          readch();
          CalcWord.le.line = line;
          tokens.add(CalcWord.le);
          return true;
        }
        break;
      case '!':
        if (readch('=')) {
          readch();
          CalcWord.ne.line = line;
          tokens.add(CalcWord.ne);
          return true;
        }
        break;
    }
    return false;
  }

  /*下面开始分割关键字,标识符等信息*/
  public Token scan() throws IOException {
    Token tok;
    while (character == ' ')
      readch();
    if (isDigit() || isSign() || isLetter()) {
      tok = tokens.get(tokens.size() - 1);
    } else {
      tok = new Token(character);
      printError(tok);
    }
    return tok;
  }
}
package Yue.LexicalAnalyzer;

/*
 * Token父类
 */
public class Token {
  public final int tag;
  public int line = 1;
  public String name = "";
  public int pos = 0;

  public Token(int t) {
    this.tag = t;
  }

  public String toString() {
    return "" + (char) tag;
  }

}
package Yue.LexicalAnalyzer;

/*
 * 单词类别赋值
 */
public class Tag {
  public final static int
      BEGIN = 1,     //保留字
      END = 2,      //保留字
      INTEGER = 3,    //保留字
      FUNCTION = 4,    //保留字
      READ = 5,      //保留字
      WRITE = 6,     //保留字
      IF = 7,       //保留字
      THEN = 8,      //保留字
      ELSE = 9,      //保留字
      SYMBOL = 11,    //标识符
      CONSTANT = 12,   //常数
      ADD = 13,      //运算符 "+"
      SUB = 14,      //运算符 "-"
      MUL = 15,      //运算符 "*"
      DIV = 16,      //运算符 "/"
      LE = 18,      //运算符 "<="
      GE = 19,      //运算符 ">="
      NE = 20,      //运算符 "!="
      ASSIGN = 23,    //运算符 ":="
      LPAR = 24,     //界符 "("
      RPAR = 25,     //界符 ")"
      SEM = 26,      //界符 ";"
      LINE_END = 27,   //行尾符
      ALL_END = 28;    //结尾符 "#"
}
package Yue.LexicalAnalyzer;

/**
 * 保留字
 */
public class KeyWord extends Token {
  public String lexme = "";

  public KeyWord(String s, int t) {
    super(t);
    this.lexme = s;
    this.name = "保留字";
  }

  public String toString() {
    return this.lexme;
  }

  public static final KeyWord
      begin = new KeyWord("begin", Tag.BEGIN),
      end = new KeyWord("end", Tag.END),
      integer = new KeyWord("integer", Tag.INTEGER),
      function = new KeyWord("function", Tag.FUNCTION),
      read = new KeyWord("read", Tag.READ),
      write = new KeyWord("write", Tag.WRITE),
      aIf = new KeyWord("if", Tag.IF),
      aThen = new KeyWord("then", Tag.THEN),
      aElse = new KeyWord("else", Tag.ELSE);
}
package Yue.LexicalAnalyzer;

/*
 * 标识符
 */
public class Symbol extends Token {
  public String lexme = "";

  public Symbol(String s) {
    super(Tag.SYMBOL);
    this.lexme = s;
    this.name = "标识符";
  }

  public String toString() {
    return this.lexme;
  }

}
package Yue.LexicalAnalyzer;

/**
 * 运算符
 */
public class CalcWord extends Token {
  public String lexme = "";

  public CalcWord(String s, int t) {
    super(t);
    this.lexme = s;
    this.name = "运算符";
  }

  public String toString() {
    return this.lexme;
  }

  public static final CalcWord
      add = new CalcWord("+", Tag.ADD),
      sub = new CalcWord("-", Tag.SUB),
      mul = new CalcWord("*", Tag.MUL),
      div = new CalcWord("/", Tag.DIV),
      le = new CalcWord("<=", Tag.LE),
      ge = new CalcWord(">=", Tag.GE),
      ne = new CalcWord("!=", Tag.NE),
      assign = new CalcWord(":=", Tag.ASSIGN);
}
package Yue.LexicalAnalyzer;

/**
 * 界符
 */
public class Delimiter extends Token {
  public String lexme = "";

  public Delimiter(String s, int t) {
    super(t);
    this.lexme = s;
    this.name = "界符";
  }

  public String toString() {
    return this.lexme;
  }

  public static final Delimiter
      lpar = new Delimiter("(", Tag.LPAR),
      rpar = new Delimiter(")", Tag.RPAR),
      sem = new Delimiter(";", Tag.SEM);
}
package Yue.LexicalAnalyzer;

/*
 * 常数
 */
public class Num extends Token {
  public final int value;

  public Num(int v) {
    super(Tag.CONSTANT);
    this.value = v;
    this.name = "常数";
  }

  public String toString() {
    return "" + value;
  }
}
package Yue.LexicalAnalyzer;

/**
 * 行尾符
 */
public class LineEnd extends Token {
  public String lexme = "";

  public LineEnd(String s) {
    super(Tag.LINE_END);
    this.lexme = s;
    this.name = "行尾符";
  }

  public String toString() {
    return this.lexme;
  }

  public static final LineEnd lineEnd = new LineEnd("\r\n");
}
package Yue.LexicalAnalyzer;

/**
 * 结尾符
 */
public class AllEnd extends Token {
  public String lexme = "";

  public AllEnd(String s) {
    super(Tag.ALL_END);
    this.lexme = s;
    this.name = "结尾符";
  }

  public String toString() {
    return this.lexme;
  }

  public static final AllEnd allEnd = new AllEnd("#");
}

总结

以上就睡这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

(0)

相关推荐

  • java 字符串词频统计实例代码

    复制代码 代码如下: package com.gpdi.action; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; public class WordsStatistics { class Obj {         int count ;         Obj(int count)

  • C#词法分析器之转换DFA详解

    在上一篇文章中,已经得到了与正则表达式等价的 NFA,本篇文章会说明如何从 NFA 转换为 DFA,以及对 DFA 和字符类进行化简. 一.DFA 的表示 DFA 的表示与 NFA 比较类似,不过要简单的多,只需要一个添加新状态的方法即可.Dfa 类的代码如下所示: 复制代码 代码如下: namespace Cyjb.Compiler.Lexer {     class Dfa {         // 在当前 DFA 中创建一个新状态.         DfaState NewState()

  • C#词法分析器之词法分析的使用详解

    虽然文章的标题是词法分析,但首先还是要从编译原理说开来.编译原理应该很多人都听说过,虽然不一定会有多么了解. 简单的说,编译原理就是研究如何进行编译--也就如何从代码(*.cs 文件)转换为计算机可以执行的程序(*.exe 文件).当然也有些语言如 JavaScript 是解释执行的,它的代码是直接被执行的,不需要生成可执行程序. 编译过程是很复杂的,它涉及到很多步骤,直接拿<编译原理>(Compilers: Principles, Techniques and Tools,红龙书)上的图来看

  • C#词法分析器之输入缓冲和代码定位的应用分析

    一.输入缓冲 在介绍如何进行词法分析之前,先来说说一个不怎么被提及的问题--怎么从源文件中读取字符流.为什么这个问题这么重要呢?是因为在词法分析中,对字符流是有要求的,它必须能够支持回退操作(就是将多个字符放回到流中,以后会再次被读取). 先来解释下为什么需要支持回退操作,举个简单的例子来说,现在要对两个模式进行匹配: 图 1 流的回退过程 上面是一个简单的匹配过程,仅为了展示回退过程,在后面实现 DFA 模拟器时会详细解释是如何匹配词素的. 现在来看看 C# 中与输入相关的类,有 Stream

  • javascript 词法作用域和闭包分析说明

    复制代码 代码如下: var classA = function(){ this.prop1 = 1; } classA.prototype.func1 = function(){ var that = this, var1 = 2; function a(){ return function(){ alert(var1); alert(this.prop1); }.apply(that); }; a(); } var objA = new ClassA(); objA.func1(); 大家应

  • C#词法分析器之构造NFA详解

    有了上一节中得到的正则表达式,那么就可以用来构造 NFA 了.NFA 可以很容易的从正则表达式转换而来,也有助于理解正则表达式表示的模式. 一.NFA 的表示方法 在这里,一个 NFA 至少具有两个状态:首状态和尾状态,如图 1 所示,正则表达式 $t$ 对应的 NFA 是 N(t),它的首状态是 $H$,尾状态是 $T$.图中仅仅画出了首尾两个状态,其它的状态和状态间的转移都没有表示出来,这是因为在下面介绍的递归算法中,仅需要知道 NFA 的首尾状态,其它的信息并不需要关心. 图 1 NFA

  • C#词法分析器之正则表达式的使用

    正则表达式是一种描述词素的重要表示方法.虽然正则表达式并不能表达出所有可能的模式(例如"由等数量的 a 和 b 组成的字符串"),但是它可以非常高效的描述处理词法单元时要用到的模式类型. 一.正则表达式的定义正则表达式可以由较小的正则表达式按照规则递归地构建.每个正则表达式 r  表示一个语言 L(r) ,而语言可以认为是一个字符串的集合.正则表达式有以下两个基本要素: 1.ϵ  是一个正则表达式, L(ϵ)=ϵ ,即该语言只包含空串(长度为 0 的字符串).2.如果 a  是一个字符

  • java使用Nagao算法实现新词发现、热门词的挖掘

    采用Nagao算法统计各个子字符串的频次,然后基于这些频次统计每个字符串的词频.左右邻个数.左右熵.交互信息(内部凝聚度). 名词解释: Nagao算法:一种快速的统计文本里所有子字符串频次的算法.详细算法可见http://www.doc88.com/p-664123446503.html   词频:该字符串在文档中出现的次数.出现次数越多越重要.   左右邻个数:文档中该字符串的左边和右边出现的不同的字的个数.左右邻越多,说明字符串成词概率越高.   左右熵:文档中该字符串的左边和右边出现的不

  • Python的词法分析与语法分析

    词法分析(Lexical Analysis):分析由字符组成的单词是否合法,如果没有问题的话,则产生一个单词流. 语法分析(Syntactic Analysis):分析由单词组成的句子是否合法,如果没有问题的话,则产生一个语法树. 在词法分析器分析源代码文本的时候,有一个概念需要明确: 1.物理行:由回车字符序列(在Windows上是CR LF,在Unix上是LF)结尾的字符序列组成一个物理行. 2.逻辑行:由一个或者多个物理行组成,可以明确地使用反斜杠(\)来连接多个物理行使之成为一个逻辑行:

  • JavaEE Filter敏感词过滤的方法实例详解

    我们在聊天的时候的或者留言的时候,有部分词是不允许发表出来.我们可以采用过滤器实现这个功能. 我们只是简单利用过滤器实现这个过滤的功能,有些地方没写的很全 前台代码: <body> <form action="<c:url value='/WordServlet'/>" method="post"> 姓名:<input type="text" name="name"/><b

随机推荐