func pushExpression(expression *parser.Node, table *SymbolTable) string { if expression == nil { panic("argument must not be nil") } if expression.Name != "expression" { panic(fmt.Sprintf("argument must be `expression`, but actual: %v", expression.ToXML())) } leftTerm, _ := expression.Find(&parser.Node{Name: "term"}) result := compileTerm(leftTerm, table) if len(expression.Children) > 1 { operator := expression.Children[1] expression.Children = expression.Children[2:] result += pushExpression(expression, table) result += compileOperator(operator.Value) } return result }
func pushStatements(statements *parser.Node, table *SymbolTable) string { result := "" for _, statement := range statements.Children { switch statement.Name { case "letStatement": identifier, _ := statement.Find(&parser.Node{Name: "identifier"}) symbol := table.Get(identifier.Value) if symbol == nil { panic(fmt.Sprintf("variable `%v` is not defined: %v\n%v", identifier.Value, table.String(), statements.ToXML())) } bracket, _ := statement.Find(&parser.Node{Name: "symbol", Value: "["}) if bracket != nil { expressions := statement.FindAll(&parser.Node{Name: "expression"}) result += pushExpression(expressions[0], table) result += pushSymbol(symbol) result += "add\n" result += pushExpression(expressions[1], table) result += "pop temp 0\n" result += "pop pointer 1\n" result += "push temp 0\n" result += "pop that 0\n" } else { expression, _ := statement.Find(&parser.Node{Name: "expression"}) result += pushExpression(expression, table) result += popSymbol(symbol) } case "doStatement": subroutineCall := &parser.Node{Name: "subroutineCall", Children: statement.Children[1 : len(statement.Children)-1]} result += compileSubroutineCall(subroutineCall, table) result += "pop temp 0\n" case "returnStatement": expression, _ := statement.Find(&parser.Node{Name: "expression"}) if expression != nil { result += pushExpression(expression, table) } else { result += "push constant 0\n" } result += "return\n" case "ifStatement": ifExpression, _ := statement.Find(&parser.Node{Name: "expression"}) ifStatementsList := statement.FindAll(&parser.Node{Name: "statements"}) trueLabel := uniqueLabel("IF_TRUE") falseLabel := uniqueLabel("IF_FALSE") endLabel := uniqueLabel("IF_END") if len(ifStatementsList) > 1 { ifStatements, elseStatements := ifStatementsList[0], ifStatementsList[1] result += pushExpression(ifExpression, table) result += "if-goto " + trueLabel + "\n" result += "goto " + falseLabel + "\n" result += "label " + trueLabel + "\n" result += pushStatements(ifStatements, table) result += "goto " + endLabel + "\n" result += "label " + falseLabel + "\n" result += pushStatements(elseStatements, table) result += "label " + endLabel + "\n" } else { ifStatements := ifStatementsList[0] result += pushExpression(ifExpression, table) result += "if-goto " + trueLabel + "\n" result += "goto " + falseLabel + "\n" result += "label " + trueLabel + "\n" result += pushStatements(ifStatements, table) result += "label " + falseLabel + "\n" } case "whileStatement": expLabel := uniqueLabel("WHILE_EXP") endLabel := uniqueLabel("WHILE_END") result += "label " + expLabel + "\n" whileExpression, _ := statement.Find(&parser.Node{Name: "expression"}) result += pushExpression(whileExpression, table) result += "not\n" result += "if-goto " + endLabel + "\n" whileBody, _ := statement.Find(&parser.Node{Name: "statements"}) result += pushStatements(whileBody, table) result += "goto " + expLabel + "\n" result += "label " + endLabel + "\n" } } return result }