2012-05-12 51 views
0

因此,对于一个项目,我试图为从文件读入的假编程语言创建一个简单的词法分析器。我在本周早些时候提出了一个问题,询问我如何实现这样一个程序,并松了一口气告诉我: 创建一个输入缓冲区和两个输出缓冲区。 初始化两个循环并递增它们直到找到标记的开始。一旦我找到了开始,增加第二个循环,直到找到一个空格或符号,然后使用case语句输出到两个输出文件,然后使外部循环等于内部并继续扫描。我做了一些研究,这种方法类似于循环和切换方法或“特设”方法。“Ad Hoc”词法分析器

import java.io.*; 

public class Lex { 

    public static boolean contains(char[] a, char b){ 
     for (int i = 0; i < a.length; i++) { 
      if(b == a[i]) 
       return true; 
     } 
     return false; 
    } 
    public static void main(String args[]) throws FileNotFoundException, IOException{ 

     //Declaring token values as constant integers. 
     final int T_DOUBLE = 0; 
     final int T_ELSE = 1; 
     final int T_IF = 2; 
     final int T_INT = 3; 
     final int T_RETURN = 4; 
     final int T_VOID = 5; 
     final int T_WHILE = 6; 
     final int T_PLUS = 7; 
     final int T_MINUS = 8; 
     final int T_MULTIPLICATION = 9; 
     final int T_DIVISION = 10; 
     final int T_LESS = 11; 
     final int T_LESSEQUAL = 12; 
     final int T_GREATER = 13; 
     final int T_GREATEREQUAL = 14; 
     final int T_EQUAL = 16; 
     final int T_NOTEQUAL = 17; 
     final int T_ASSIGNOP = 18; 
     final int T_SMEICOLON = 19; 
     final int T_PERIOD = 20; 
     final int T_LEFTPAREN = 21; 
     final int T_RIGHTPAREN = 22; 
     final int T_LEFTBRACKET = 23; 
     final int T_RIGHTBRACKET = 24; 
     final int T_LEFTBRACE = 25; 
     final int T_RIGHTBRACE = 26; 
     final int T_ID = 27; 
     final int T_NUM = 28; 
     char[] letters_ = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D', 
      'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','_'}; 
     char[] numbers = {'0','1','2','3','4','5','6','7','8','9'}; 
     char[] symbols = {'+','-','*','/','<','>','!','=',':',',','.','(',')','[',']','{','}'}; 
     FileInputStream fstream = new FileInputStream("src\\testCode.txt"); 
     DataInputStream in = new DataInputStream(fstream); 
     BufferedReader br = new BufferedReader(new InputStreamReader(in)); 
     BufferedWriter bw1 = new BufferedWriter(new FileWriter(new File("src\\output.txt"), true)); 
     BufferedWriter bw2 = new BufferedWriter(new FileWriter(new File("src\\output2.txt"), true)); 
     String scanner;String temp = ""; 
     int n = 0; 
     while((scanner = br.readLine()) != null){ 
      for (int i = 0; i < scanner.length(); i++) { 
       for (int j = 0; j < scanner.length(); j++) { 
        if(contains(letters_,scanner.charAt(i)) || contains(numbers,scanner.charAt(i)) || contains(symbols,scanner.charAt(i))){ 
         j++; 
         n++; 
         if(scanner.charAt(j) == ' ' || scanner.charAt(j) == '\n' || scanner.charAt(j) == '\t'){ 

         } 
        } 

       } 

      } 
     } 

     in.close(); 


    } 

} 

我的问题是如何确定在找到空格或符号后指定一个词的标记。我可以把每个字符放在一个字符串中的ws和符号之前,然后像这样比较吗?我尝试过类似的东西,但是它将整个输入文件写入字符串,所以我的令牌在我的switch语句中不匹配。同样使用这种方法,我怎样才能安全地忽略注释和注释块,因为它们不应该被标记。

回答

0

这取决于您需要使用词法分析器的复杂程度。如果你现在正在分割空白,你可以简单地将每个词位与一系列正则表达式进行比较,看看哪一个与它匹配。这是一种简单的做法,效率不高,但这可能不会影响您的决定。

“真正的”词法分析器通常用作有限自动机。如果你知道如何构建一个能够识别正则表达式的自动机,你可以将其中的几个组合成一个更大的自动机,以识别O(1)复杂性中的几个表达式。如果有兴趣,我已经写了一个关于这个主题的series of articles。这是一项复杂而有益的任务。

+0

我不认为词法分析器很复杂,只是我接近它的方式是。我只需要基于我是否得到标识符,特殊关键字,sysmbol或整数/小数来分开令牌。 – Thomas

+0

是的,你的方法将起作用。只需将你从分隔符中得到的任何内容与一堆正则表达式进行比较,看看哪些匹配,然后选择最高优先级的令牌类型。 – Dervall

+0

我可以使用java.util.regex吗?或者用if语句遍历每个字符。例如:if(scanner.chatAt(i)== a || == b)。 (我有一个方法告诉我,如果字符是一个字母或_)我只是没有看到如何使用正则表达式对个别字符。 – Thomas

1

构建词法分析器的经典方法是通过循环内的switch语句。基本思想是每个字符处理一次,而不是重新扫描。案例A到Z和a到z可以开始一个标识符,因此这些案例必须吸收所有可能的标识符字符,直到您找到一个不是的标识符字符,将它们组装成标识符标记并将IDENTIFIER返回给调用者。类似的情况下0到9可以开始一个数字,所以你吸入数字并返回INTEGER或DOUBLE或其它。例如空格,制表符,换行符,换页符等等都是空格,所以将所有的空格都填满并继续外循环而不返回。所有其他的都是标点符号,所以你把它们吸引过来,从两个字符的字符中挑出一个字符值,并且通常为单字符字符值返回字符值,为其他字符值赋予一个特殊的标记值。不要忘记正确处理EOF :-)调整案例和规则以适应您正在分析的语言。

+0

我如何处理这种方法的意见?如果我扫描/ /我只是跳到我正在阅读的输入文件的下一行?和相同的评论块..只是吸字符,直到我打* /? – Thomas

+0

这是正确的。一旦你开始,你会发现它很明显。在像lex/flex这样的基于DFA的词法分析器出现之前,这就是编译器扫描器总是如何实现的。它比使用多个正则表达式更有效率。 – EJP

+0

另外,我应该在我之前的评论中包含这个,以便“吸取”我在一个案例中制作循环的字符,然后在我返回一个令牌后将外部循环发送到在该案例中制作的循环中?如果我使用字符串生成器将文件存储在一行上,我如何处理注释行?因为我不能跳到下一行 – Thomas