public override void Assignment(Token varName, bool withArrayIndex) { Output.WriteLine( "ASSIGNMENT TO {0}{1}", varName.Value, withArrayIndex ? "[...]" : ""); }
public override void Assignment(Token varName, bool withArrayIndex) { //This is slightly tricky. We need to have the address of the //variable to which we're doing the assignment on the stack, //and then everything is really easy. If it's an array, we need //the address of the array element to which we're doing the //assignment (its index is on the stack following the LHS value). if (MethodSymTable.HasSymbol(varName.Value)) { Symbol symbol = MethodSymTable.GetSymbol(varName.Value); Contract.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter); Output.WriteLine(" __PUSH((__WORD)&{0});", symbol.Name); } else { Contract.Assert(ClassSymTable.HasSymbol(varName.Value)); Symbol symbol = ClassSymTable.GetSymbol(varName.Value); if (symbol.Kind == SymbolKind.Static) { Output.WriteLine(" __PUSH((__WORD)&{0});", FormatStaticName(symbol.Name)); } else if (symbol.Kind == SymbolKind.Field) { Output.WriteLine(" __PUSH((__WORD)&((({0}*)THIS)->{1}));", _currentClassName, symbol.Name); } } //If it's an array, obtain the address of the right element by //adding the index which is on the stack. We need a scratch //location because issuing two __POP() calls in the same statement //does not guarantee left-to-right evaluation. if (withArrayIndex) { //The array address is now on the stack, but we really need the //address of the first element. Hence the dereference: Output.WriteLine(" __SCRATCH2 = *(__WORD*)__POP();"); //This is the RHS value that we ought to put in the array element: Output.WriteLine(" __SCRATCH1 = __POP();"); //Finally, the top of the stack contains the value of the array //indexing expression, i.e. the element index: Output.WriteLine(" * ( ((__WORD*)__SCRATCH2) + __POP() ) = __SCRATCH1;"); } else { Output.WriteLine(" __SCRATCH1 = __POP();"); //This is the LHS Output.WriteLine(" * ((__WORD*)__SCRATCH1) = __POP();"); } }
public abstract void VariableRead(Token varName, bool withArrayIndex);
public abstract void Assignment(Token varName, bool withArrayIndex);
public void Advance() { EatWhitespace(); if (IsAtEnd) { _done = true; return; } char nextChar = NextChar(); if (Syntax.IsSymbol(nextChar)) { //This token is going to be a symbol. There are three special //look-ahead cases for '<=', '>=', and '!='. if ((new[] { '<', '>', '!' }.Contains(nextChar)) && LookAhead() == '=') { NextChar();//Eat the '=' _currentToken = new Token(TokenType.Symbol, nextChar + "="); } else { _currentToken = new Token(TokenType.Symbol, nextChar.ToString()); } } else if (Syntax.IsNumber(nextChar)) { //This token is going to be an integer constant. string intConst = nextChar.ToString(); intConst += EatWhile(Syntax.IsNumber); int result; if (!int.TryParse(intConst, out result)) { throw new CompilationException( "Integer constant must be in range [0,2147483648), but got: " + intConst, _currentLine); } _currentToken = new Token(TokenType.IntConst, intConst); } else if (Syntax.IsCharOrdinalStart(nextChar)) { //This is an addition to the 'standard' Jack language. We //support also character ordinals of the form 'H' or of the //form '\nnn' where nnn are decimal digits. They are translated //to integer constants as far as the parser is concerned. char marker = NextChar(); if (marker == '\\') { string code = EatWhile(Syntax.IsNumber); if (code.Length != 3) { throw new CompilationException("Expected: \\nnn where n are decimal digits", _currentLine); } int value = int.Parse(code); if (value >= 256) { throw new CompilationException("Character ordinal is out of range [0,255]", _currentLine); } _currentToken = new Token(TokenType.IntConst, value.ToString()); } else { _currentToken = new Token(TokenType.IntConst, ((int)marker).ToString()); } NextChar();//Swallow the end of the character ordinal } else if (Syntax.IsStringConstantStart(nextChar)) { //This token is going to be a string constant. string strConst = EatWhile(c => !Syntax.IsStringConstantStart(c)); NextChar();//Swallow the end of the string constant _currentToken = new Token(TokenType.StrConst, strConst); } else if (Syntax.IsStartOfKeywordOrIdent(nextChar)) { //This is going to be a keyword or an identifier. We can't //tell what it's going to be until we accumulate more of it. //We need to read until we encounter whitespace. string keywordOrIdent = nextChar.ToString(); keywordOrIdent += EatWhile(Syntax.IsPartOfKeywordOrIdent); if (Syntax.IsKeyword(keywordOrIdent)) { _currentToken = new Token(TokenType.Keyword, keywordOrIdent); } else { _currentToken = new Token(TokenType.Ident, keywordOrIdent); } } else { throw new CompilationException("Unexpected character: " + nextChar, _currentLine); } }
private void VerifyArrayAccessAllowed(Token varName) { Symbol variable = GetClosestSymbol(varName.Value); if (variable.Type != "Array") { ThrowCompilationException("Only arrays may be indexed with '[]'"); } }
//sub-call ::= <sub-name> '(' <expr-list> ')' | // (<class-name> | <var-name>) '.' <sub-name> '(' <expr-list> ')' //expr-list ::= (<expression> (',' <expression>)*)? private void ParseSubCall(Token firstPart = null) { //NOTE: Because the parser may inadvertently swallow the first part //of the subroutine call expression (i.e. the subroutine name, //class name, or variable name), there's an option for this method //to accept it explicitly instead of reading it from the tokenizer. // //After this method completes, the code generator should have //available to it the result of the subroutine's invocation (e.g. //on the stack or in a predefined register). If the subroutine //call is not part of an expression (i.e. it's part of a 'do' //statement), then this result should be discarded via an explicit //call to the code generator. if (firstPart == null) { firstPart = NextToken(); } if (firstPart.Type != TokenType.Ident) { ThrowCompilationException("Expected an identifier, got: " + firstPart.Value); } string classNameOfSubroutine = null; string subroutineName = null; bool isInstanceMethod = false; Symbol classOrVarName = GetClosestSymbol(firstPart.Value); if (classOrVarName == null) { if (LookAheadToken.Value == "(") { //If this is the case, this is a direct method call within //the current class. The current subroutine has to be //non-static (i.e. a method or a constructor) for us to //dispatch a method call. Because we're a one-pass compiler, //we can't verify that the method actually exists. if (_currentSub.Kind != SubroutineKind.Constructor && _currentSub.Kind != SubroutineKind.Method) { ThrowCompilationException("Method calls are allowed only within a method or a constructor"); } classNameOfSubroutine = _currentClassName; subroutineName = firstPart.Value; isInstanceMethod = true; } else if (LookAheadToken.Value == ".") { //This is a function (or constructor) call on a class name. //We don't know about all class names, so we have to assume //that this class exists. The assembler will tell us if //we're wrong. Alternatively, we could implement a multi- //pass compiler that goes over only declarations, or require //all subroutines and classes to be declared before they //can be called. //NOTE: If we want to do inlining as an optimization, we //must perform two passes (or else we don't know what's in //the subroutine we're placing inline :-)). Match(new Token(TokenType.Symbol, ".")); Token subName = NextToken(); if (subName.Type != TokenType.Ident) { ThrowCompilationException("Expected an identifier, got: " + subName.Value); } classNameOfSubroutine = firstPart.Value; subroutineName = subName.Value; isInstanceMethod = false; } else { ThrowCompilationException("Unexpected: " + LookAheadToken.Value); } } else { //This is a method call on a variable. We must be able to tell //the class name by looking at the variable's type. Note that //built-in types that we recognize as keywords don't have any //methods, so this is an error we can flag easily. classNameOfSubroutine = classOrVarName.Type; if (Syntax.IsKeyword(classNameOfSubroutine)) { ThrowCompilationException("Can't call methods on built-in types"); } Match(new Token(TokenType.Symbol, ".")); Token subName = NextToken(); if (subName.Type != TokenType.Ident) { ThrowCompilationException("Expected an identifier, got: " + subName.Value); } subroutineName = subName.Value; isInstanceMethod = true; } Match(new Token(TokenType.Symbol, "(")); //If this is an instance method, we need to supply 'this' as the //first parameter to be extracted on the other side. if (isInstanceMethod) { if (classNameOfSubroutine == _currentClassName) { //'this' is actually our very own instance, as we're calling //an instance method on ourselves. _codeGenerator.This(); } else { //'this' is actually the variable on which we're making //the call, so we need that pushed. _codeGenerator.VariableRead(firstPart, false); } } ParseExpressionList(); Match(new Token(TokenType.Symbol, ")")); _codeGenerator.Call(classNameOfSubroutine, subroutineName); //TODO: Detect the situation where a void subroutine is called //inside an expression where a value has to be provided. This is //impossible without having the subroutine's declaration in front //of us. To alleviate this, we can have all subroutines, even void //ones, return some arbitrary value (e.g. 0). }
private void MatchCurrent(Token token) { if (_tokenizer.LookAheadToken.Type != token.Type || _tokenizer.LookAheadToken.Value != token.Value) { Expected(token.Value); } }
private void Match(Token token) { if (!_tokenizer.HasNext) Expected(token.Value); Token actual = _tokenizer.Next(); if (actual.Type != token.Type || actual.Value != token.Value) { Expected(token.Value); } }
public override void VariableRead(Token varName, bool withArrayIndex) { Output.WriteLine( "READ FROM {0}{1}", varName.Value, withArrayIndex ? "[...]" : ""); }
public override void VariableRead(Token varName, bool withArrayIndex) { //Put the value of the variable on the top of the stack. If it's //an array, the value is the address of the array's first element. if (MethodSymTable.HasSymbol(varName.Value)) { Symbol symbol = MethodSymTable.GetSymbol(varName.Value); Contract.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter); Output.WriteLine(" __PUSH({0});", symbol.Name); } else { Contract.Assert(ClassSymTable.HasSymbol(varName.Value)); Symbol symbol = ClassSymTable.GetSymbol(varName.Value); if (symbol.Kind == SymbolKind.Static) { Output.WriteLine(" __PUSH({0});", FormatStaticName(symbol.Name)); } else if (symbol.Kind == SymbolKind.Field) { Output.WriteLine(" __PUSH((({0}*)THIS)->{1});", _currentClassName, symbol.Name); } } //If it's an array, dereference it using []. We need a scratch //location because issuing two __POP() calls in the same statement //does not guarantee left-to-right evaluation. if (withArrayIndex) { Output.WriteLine(" __SCRATCH1 = __POP();"); Output.WriteLine(" __PUSH( ((__WORD*)__SCRATCH1)[ __POP() ] );"); } }