如何使用util.regex.Matcher(java)创建Tokenizer?

时间:2014-07-16 05:31:40

标签: java regex matcher

其实我有以下正则表达式令牌(学校职责):

Identificator = [a-zA-Z_][a-zA-Z0-9_]*
Integer = [0-9]+
ReservedKeywords = true|false|while|foreach|for|plus
Symbols = *|/|-|\(|\)|
Blank = \s+

我无法使用Scanner类,因为某些令牌之间可能没有空格。请注意,Parser(给定它接收正确的令牌)已准备好,并且还要进行类型检查和AST评估。只有最简单的"部分缺失所以" Tokenizer"互联网上没有足够的完整示例。

我不了解util.regex.Matcher类的文档,这非常令人困惑。

实际上是合法的

  • [ ReservedKeyword |识别器|整数] 后跟一个符号
  • 符号后跟 [ ReservedKeyword |识别器|整型|符号]
  • [ ReservedKeyword |识别器|整型|符号] 后跟空白
  • 空白后跟 [ ReservedKeyword |识别器|整型|符号]
  • [ ReservedKeyword |识别器|整型|符号|空白] ,然后是End of Stream / String

我们必须使用Matcher类,所以无论如何都没有机会对tokenizer进行硬编码(这太简单了:简单的状态机+地图查找,但我们不允许这样做。)

标记器必须有2个方法(" hasNext"" next")。

我需要一些示例来了解如何使用Matcher将字符串与依赖于上下文的分隔符匹配(Scanner类不适合,因为将" eat"分隔符,而分隔符是语法的一部分见下面的例子:

(3 plus 5)*(8/3*7)

它应该被标记为

(.3.plus.5.).*.(.8./.3.*.7.)

我可以使用"(|)| \ s +"作为分隔符,但扫描仪将返回

3 plus 5 * 8 / 3 * 7

由于运营商的关联性,结果将是

3 plus (((5*8)/3)*7)

这是不正确的。

我需要做以下事情:

给定一组模式(Identificator,Integer,ReservedKeywords,Symbols,Blank等等)。我需要匹配任何这些模式的第一次出现。分隔符是"符号|空白" 但是不应该丢弃分隔符,而应该将它们作为代币返回。 这必须使用Matcher类完成。

一个示例,说明如何使用as分隔符标记字符串" Blank |符号"返回或分隔的字符串或分隔符本身就足够了。

1 个答案:

答案 0 :(得分:0)

在花了很多时间弄清楚Matcher是如何工作之后,我能够创建一个比通常的Scanner更复杂的标记器。由于没有人回答这是相关部分(因为这是学校作业,我可以分享代码):

private final Scanner scanner;
private final String delimiter =  "\\*|/|-|\\(|\\)|";

private final Pattern delim  = Pattern.compile(delimiter);
private Matcher delim_matcher;

private String  region;
private int     regionStart;
private int     regionEnd;
private int     start;
private int     end;

// Called by constructor, I stripped the constructor because trivial
private void Init(){
    scanner = new Scanner(System.in);
    region = scanner.next();
    delim_matcher = delim.matcher(region);
    regionStart = 0;
    regionEnd = region.length();
}

private boolean nextDelimiter(){
    boolean found = delim_matcher.find();
    start = found ? delim_matcher.start() : delim_matcher.regionEnd();
    end = found? delim_matcher.end(): delim_matcher.regionEnd();
    return found;
}

private boolean hasPrefix(){
    return start > regionStart;
}

public TokenType next() throws NoSuchElementException{
    //find next delimiter ( symbol )
    boolean found = nextDelimiter(); //TODO: see breakpoints here

    if(hasPrefix()){

        //there was something before the delimiter (keyword, identificator etc.)
        decodePrefix( region.substring(regionStart,start) );

        if(found)
            delim_matcher.region(start,regionEnd); //reset to match symbol next time

        regionStart = start; //hasPrefix -> false
        return tokenType;   
    }
    else if(!hasPrefix() && found){

        decodeSymbol( region.substring(start,end)); 
        delim_matcher.region(end,regionEnd); //reset to skip already found symbol
        regionStart = end;
        return tokenType;
    }
    else{

        if(scanner.hasNext()){ //next is not a whitespace (because scanner already skip blanks)
            region = scanner.next(); 
            delim_matcher = delim.matcher(region);
            regionStart = 0;
            regionEnd = region.length();
            return next();
        }else
            return tokenType = EOF;
    }
}

public boolean hasNext() {
    return tokenType != EOF; //EOF is a value of the enum "TokenType"
}

正如预期的那样,这个Tokenizer比Scanner类更有用。 Scanner类具有丢弃分隔符的缺点(因为在解析程序时符号可能是分隔符,我不希望它们被丢弃)。

此Tokenizer使用扫描程序检索空白分隔字符串,然后使用其他处理将字符串分割为符号。