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);
예제 #5
0
        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);
            }
        }
예제 #6
0
 private void VerifyArrayAccessAllowed(Token varName)
 {
     Symbol variable = GetClosestSymbol(varName.Value);
     if (variable.Type != "Array")
     {
         ThrowCompilationException("Only arrays may be indexed with '[]'");
     }
 }
예제 #7
0
        //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).
        }
예제 #8
0
 private void MatchCurrent(Token token)
 {
     if (_tokenizer.LookAheadToken.Type != token.Type ||
         _tokenizer.LookAheadToken.Value != token.Value)
     {
         Expected(token.Value);
     }
 }
예제 #9
0
        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() ] );");
            }
        }