//let-statement ::= 'let' <var-name> ('[' <expression> ']')? '=' <expression> ';' private void ParseLetStatement() { Match(new Token(TokenType.Keyword, "let")); Token varName = NextToken(); if (GetClosestSymbol(varName.Value) == null) { ThrowCompilationException("Undefined variable '" + varName.Value + "'"); } if (!_methodSymTable.HasSymbol(varName.Value)) { Symbol variable = _classSymTable.GetSymbol(varName.Value); if (variable.Kind == SymbolKind.Field && _currentSub.Kind != SubroutineKind.Constructor && _currentSub.Kind != SubroutineKind.Method) { ThrowCompilationException("Instance fields may be used only from a method or a constructor"); } } bool withArrayIndex = false; if (LookAheadToken.Value == "[") { VerifyArrayAccessAllowed(varName); withArrayIndex = true; Match(new Token(TokenType.Symbol, "[")); ParseExpression(); Match(new Token(TokenType.Symbol, "]")); } Match(new Token(TokenType.Symbol, "=")); ParseExpression(); _codeGenerator.Assignment(varName, withArrayIndex); Match(new Token(TokenType.Symbol, ";")); //TODO: Type checking, but this is really hard . . . (we need //to propagate the type of the LHS expression so that we can //verify that it's the same as the RHS, and if RHS is an array //access then we have no way of knowing what should come from the //array, unless we start tracking all array types and not just //use 'Array' for all arrays) }