实际上我有以下正则表达式 token (学校职责):
Identificator = [a-zA-Z_][a-zA-Z0-9_]*
Integer = [0-9]+
ReservedKeywords = true|false|while|foreach|for|plus
Symbols = *|/|-|\(|\)|
Blank = \s+
我无法使用 Scanner 类,因为某些标记之间可能没有空格。请注意,解析器(假定它接收到正确的标记)已准备就绪,并且还可以对 AST 进行类型检查和评估。仅缺少“最简单”的部分,因此“Tokenizer”并且互联网上没有足够完整的示例。
我不明白 util.regex.Matcher 类的文档,它非常令人困惑。
实际上是合法的
- [ 保留关键字|标识符|整数]后跟一个符号
- 符号后跟[保留关键字|标识符|整数|符号]
- [ 保留关键字|标识符|整数|符号 ] 后跟一个空格
- 空格后跟 [ 保留关键字|标识符|整数|符号]
- [ 保留关键字|标识符|整数|符号|空白],后跟流/字符串结尾
我们必须使用 Matcher 类,因此无论如何都没有机会对分词器进行硬编码(这太简单了:简单的状态机+映射查找,但我们不允许这样做)。
分词器必须有 2 个方法(“hasNext”和“next”)。
我需要一些示例来了解如何使用匹配器将字符串与取决于上下文的分隔符进行匹配(Scanner 类不适合,因为它会“吃掉”分隔符,而分隔符是语法的一部分,请参见以下示例:
(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)
这是不正确的。
我需要执行以下操作:
给定一组模式(标识符、整数、保留关键字、符号、空白等)。我需要匹配这些模式中第一次出现的情况。分隔符是“符号|空白” 但分隔符不应被丢弃,而是应作为标记返回。 这必须使用 Matcher 类来完成。
展示如何使用返回的分隔符“Blank | Symbols”或分隔字符串或分隔符本身来标记字符串就足够了。
最佳答案
在花了很多时间弄清楚匹配器的工作原理之后,我能够创建一个比通常的扫描器更复杂的标记器。由于没有人回答,这是相关部分(因为这是学校作业,我可以分享代码):
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 类的缺点是丢弃分隔符(因为在解析程序时符号可能是分隔符,我不希望它们被丢弃)。
此分词器使用扫描器检索空白分隔字符串,然后使用其他处理来分割符号周围的字符串。
关于java - 如何使用 util.regex.Matcher (java) 创建 Tokenizer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24772860/