//use to return the kind index privatestaticintclassNum=0; privatestaticintargNum=0; privatestaticintlocalNum=0;
//and for subroutine, we store their argNum and localNum privatestatic Map<String, Integer> subroutineArgNum = newHashMap<>(); privatestatic Map<String, Integer> subroutineLCLNum = newHashMap<>();
privatestaticvoiddealClassVar() { if (Tokenizer.getNextString().equals("field") || Tokenizer.getNextString().equals("static")) { // get the kind StringclassVarKind= Tokenizer.stringAdvance(); // get the type StringclassVarType= Tokenizer.stringAdvance(); // get the name StringclassVarName= Tokenizer.stringAdvance(); // add to correspond kind if (classVarKind.equals("static")) { classVar.put(classVarName, classNum++); typeOfClassVar.put(classVarName, classVarType); kindOfClassVar.put(classVarName, "this"); } elseif (classVarKind.equals("field")) { classVar.put(classVarName, classNum++); typeOfClassVar.put(classVarName, classVarType); kindOfClassVar.put(classVarName, "this"); } // if there's a ',', there remains some vars while (Tokenizer.getNextString().equals(",")) { Tokenizer.stringAdvance(); StringnextName= Tokenizer.stringAdvance(); // after the operation, there will be ',' or ';' if (classVarKind.equals("static")) { classVar.put(nextName, classNum++); typeOfClassVar.put(nextName, classVarType); kindOfClassVar.put(nextName, "this"); } elseif (classVarKind.equals("field")) { classVar.put(nextName, classNum++); typeOfClassVar.put(nextName, classVarType); kindOfClassVar.put(nextName, "this"); } } // then we meets ';', we skip it Tokenizer.stringAdvance(); // we continuously deal with rest classVar recursively if (Tokenizer.getNextString().equals("field") || Tokenizer.getNextString().equals("static")) { dealClassVar(); return; } // finally, update the current string currentStringName.setLength(0); currentStringName.append(Tokenizer.stringAdvance()); } }
privatestaticvoiddealFuncArgVar() { //the function features: Dec + type + funcName + ( + args + ) //so we need to ignore the irrelevant elements StringsubroutineType= Tokenizer.stringAdvance(); currentSubroutineName.setLength(0); currentSubroutineName.append(className + "."); currentSubroutineName.append(Tokenizer.stringAdvance()); Tokenizer.stringAdvance();
//we continue read the arguments until we meet ")" while (!Tokenizer.getNextString().equals(")")) { // type + name Stringtype= Tokenizer.stringAdvance(); Stringname= Tokenizer.stringAdvance(); subroutineVar.put(name, argNum++); typeOfSubroutineVar.put(name, type); kindOfSubroutineVar.put(name, "argument"); //if there's a ',', then we have more args while (Tokenizer.getNextString().equals(",")) { //ignore the ',' Tokenizer.stringAdvance(); type = Tokenizer.stringAdvance(); name = Tokenizer.stringAdvance(); subroutineVar.put(name, argNum++); typeOfSubroutineVar.put(name, type); kindOfSubroutineVar.put(name, "argument"); } } //we ignore the ")" Tokenizer.stringAdvance(); //then we ignore the "{" Tokenizer.stringAdvance(); currentStringName.setLength(0); currentStringName.append(Tokenizer.stringAdvance()); }
privatestaticvoiddealFuncLCLVar() { if (currentStringName.toString().equals("var")) { //var + type + name //we skip var first Stringtype= Tokenizer.stringAdvance(); Stringname= Tokenizer.stringAdvance();
if (currentStringName.toString().equals("method") || currentStringName.toString().equals("function") || currentStringName.toString().equals("constructor")) { //deal with the arguments first startSubroutine(); if (currentStringName.toString().equals("method")) startMethod(); dealFuncArgVar();
//after the argument comes the body of func //we only care about varDec if (currentStringName.toString().equals("var")) dealFuncLCLVar();
//we copy the current ArrayList and deliver the copyList Map<String, Integer> copyVar = newHashMap<>(subroutineVar); Map<String, String> copyType = newHashMap<>(typeOfSubroutineVar); Map<String, String> copyKind = newHashMap<>(kindOfSubroutineVar);
//store the index of arg and lcl subroutineLCLNum.put(currentSubroutineName.toString(), localNum); subroutineArgNum.put(currentSubroutineName.toString(), argNum);
//if it's a classVar, then subroutine is null publicstaticintkindCount(String kindName, String subroutine) { try { if (subroutine.equals("Output.printInt")) return2; if (kindName.equals("argument")) return subroutineArgNum.get(subroutine); if (kindName.equals("local")) return subroutineLCLNum.get(subroutine); if (kindName.equals("field") || kindName.equals("static")) return classNum; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("The kind is out of range."); } return -1; }
2.VMWriter实现
为满足CompileEngineVM代码的生成,我们在VMWriter中设计以下函数:
1 2 3 4 5 6 7 8 9
publicstatic String writePush(String segment, int index) publicstatic String writePop(String segment, int index) publicstatic String writeOp(String command) publicstatic String writeUnaryOp(String command) publicstatic String writeCall(String name, int argNum) publicstatic String writeFunctionDec(String name, int lclNum) publicstatic String writeFunctionCall(String name, int argNum) publicstatic String writeReturn() publicstatic ArrayList<String> writeString(String string)
//if the first character belongs unary op //then we skip it StringunaryOp=""; if (isOp(line.charAt(0))) { unaryOp = Character.toString(line.charAt(0)); line = line.substring(1); }
if (!unaryOp.isEmpty()) expression.add(VMWriter.writeUnaryOp(unaryOp));
//if the first character belongs unary op //then we skip it StringunaryOp=""; if (isOp(line.charAt(0))) { unaryOp = Character.toString(line.charAt(0)); line = line.substring(1); }
if (!containOP(line)) { ArrayList<String> term = compileTerm(line); expression.addAll(term); if (!unaryOp.isEmpty()) expression.add(VMWriter.writeUnaryOp(unaryOp)); return expression; }
//if we meet (, deal with it first if (line.charAt(0) == '(') { Stack<Integer> stack = newStack<>(); intrightIndex= -1; for (inti=1; i < line.length(); i++) { if (line.charAt(i) == '(') stack.push(i); if (line.charAt(i) == ')') { if (!stack.isEmpty()) stack.pop(); else { rightIndex = i; break; } } }
StringtermExp= line.substring(0, rightIndex + 1); expression.addAll(compileTerm(termExp)); if (!unaryOp.isEmpty()) expression.add(VMWriter.writeUnaryOp(unaryOp));
//if the ) is not the last, then after it comes an op //it's defined by the Jack grammar if (rightIndex < line.length() - 1) { expression.addAll(compileExpression(line.substring(rightIndex + 2))); expression.add(VMWriter.writeOp(Character.toString(line.charAt(rightIndex + 1)))); } return expression; }
//we deal with the next op intopIndex= -1; StringcurOp=""; for (inti=0; i < line.length(); i++) { if (isOp(line.charAt(i))) { opIndex = i; curOp = Character.toString(line.charAt(i)); break; } }
StringtermExp1= line.substring(0, opIndex); StringtermExp2= line.substring(opIndex + 1); expression.addAll(compileTerm(termExp1)); if (!unaryOp.isEmpty()) expression.add(VMWriter.writeUnaryOp(unaryOp)); if (!termExp2.isEmpty()) expression.addAll(compileExpression(termExp2)); expression.add(VMWriter.writeOp(curOp)); return expression; }
privatestatic ArrayList<String> compileTerm(String line) { ArrayList<String> term = newArrayList<>();
if (line.matches("-?\\d+")) { term.add(VMWriter.writePush("constant", Integer.parseInt(line))); return term; }
if (line.contains("(")) { //if the first is "(", then it's (expression) if (line.charAt(0) == '(') { term.addAll(compileExpression(line.substring(1, line.length() - 1))); return term; } else { //else, it's a functionCall ArrayList<String> functionCall = compileSubroutineCall(line); term.addAll(functionCall); return term; } }
//if line contains '[', it must be an array if (line.contains("[")) { term.addAll(compileArray(line)); return term; }
//if it's a stringConstant, we use string method to append char by char if (line.charAt(0) == '"') { Stringstring= line.substring(1, line.length() - 1); term.addAll(VMWriter.writeString(string)); return term; }
另:我们不能直接用类中定义的static int ifLabelCount作为标记的序号,因为当存在嵌套条件循环时,由于不管遇到哪个if该值都会增加1,这会导致同一个if的前后标记序号不一致。对此,我们用一个\(Map\)来存储特定lineIndex对应的标记序号,由于lineIndex是唯一的,我们的前后标记序号将会一致。
// we first store the functionName StringfuncName= line.substring(0, line.indexOf("(")); line = line.substring(line.indexOf("(")); if (!funcName.contains(".")) funcName = className + "." + funcName;
// then comes the expressionList StringexpressionList= line.substring(line.indexOf('(') + 1, line.indexOf(')')); if (!expressionList.isEmpty()) { ArrayList<String> expressions = compileExpressionList(expressionList); subroutine.addAll(expressions); }
if (!expressionList.isEmpty()) { argNum = 1; for (inti=0; i < expressionList.length(); i++) { if (expressionList.charAt(i) == ',') argNum++; } }
// finally we call the method if (Tokenizer.funcCollection.contains(funcName)) { subroutine.add(VMWriter.writeFunctionCall(funcName, argNum)); } else { for (String s : Tokenizer.funcCollection) { if (s.substring(s.indexOf(".") + 1).equals(funcName.substring(funcName.indexOf(".") + 1))) { subroutine.add(VMWriter.writeFunctionCall(s, argNum)); } } }
if (funcType.get(funcName).equals("void")) subroutine.add(VMWriter.writePop("temp", 0));
Stringline= Operation.modify(reader.readLine()); while (line != null) { if (!line.isEmpty()) { if (line.indexOf("class") == 0) { className.setLength(0); className.append(line.substring(5, line.length() - 1)); line = Operation.modify(reader.readLine()); continue; }
if (isSubroutineDec(line)) { StringsubroutineKind=""; StringsubroutineType=""; intsubroutineLength= -1; inttypeLength= -1;
for (inti=0; i < subroutineDec.length; i++) { if (line.indexOf(subroutineDec[i]) == 0) { subroutineKind = subroutineDec[i]; subroutineLength = subroutineDec[i].length(); break; } }
line = line.substring(subroutineLength);
// then comes the type for (inti=0; i < type.length; i++) { if (line.indexOf(type[i]) == 0) { subroutineType = type[i]; typeLength = type[i].length(); break; } }