[Be sure to read tungsten's comment after this post — it is a fairly easy addition to this mod to make "do" optional as well.]
This project is to make the let keyword optional in the Jack Compiler.  
It is easy to make your jack compiler behave similarly to modern languages 
that use assignments written without a specific keyword.
i = 42;
while (i > 0) {
    do something();
    i = i-1;
}
 
Syntax changes
The book's syntax for 
let statements is:
    | statement: |   | 
    letStatement | ifStatement | whileStatement | doStatement | returnStatement | 
    | letStatement: |  | 
    'let' varName ( '[' expression ']' )? 
    '=' expression ';' | 
The required change is to add an assignment construct that replaces the 
assignment portion of letStatement, and also add assignment as an  
option in statement.
    | statement: |   | 
    assignment ';' | letStatement | ifStatement | whileStatement | doStatement | returnStatement | 
    | letStatement: |  | 
    'let' assignment ';' | 
    | assignment: |  | 
    varName ( '[' expression ']' )? 
    '=' expression | 
Note: LetStatement could have been changed by simply making the "let" 
optional — ( 'let' )? — but adding assignment 
better matches how a recursive descent compiler will need to parse the new syntax.
Code changes
All changes are limited to CompilationEngine module.
Most of compileLet() moves into compileAssignment().  All that's 
left in compileLet() is dealing with the "let" keyword and calling 
compileAssignment().
Your compileStatements() is probably structured something like this:
while tokenizer.tokenType() is KEYWORD {
    switch tokenizer.keyWord() {
        case LET:
            compileLet()
        case DO:
            compileDo()
        ...
        default:
            throw SyntaxException("Statement expected")
    }
}
It needs to detect when the current token is IDENTIFIER and call
compileAssignment() instead of switching on the keyword value.
while tokenizer.tokenType() is KEYWORD or IDENTIFIER {
    if tokenizer.tokenType() is IDENTIFIER
        compileAssignment()
    else {
        switch tokenizer.keyWord() {
        ...
compileStatements() is called from multiple places.  If your compiler 
checks that the current token is KEYWORD before calling 
compileStatements(), you will also need to check for IDENTIFIER 
in those places.
Test code
/**
 *  Test code for Jack with optional "let"
 */
class Main {
    function void main() {
        var int i, j, k;    // function with vars starts with assignment
        i = 1;
        let j = 1;
        j = j+1;            // tests that assignments and commands can be mixed
        if (true) {
            i = i+20;       // if block starts with assignment
            let j = j+20;
            j = j+10;
        } else {
            let i = -9999;  // else block starts with command
            j = -8888;
        }
        if (false) {
            let i = -9999;  // if block starts with command
            j = -8888;
        } else {
            i = i+300;      // else block starts with assignment
            let j = j+300;
            j = j+100;
        }
        while (k = 0) {
            i = i+4000;     // while block starts with assignment
            let j = j+4000;
            j = j+1000;
            let k = 1;
        }
        while(false) {
            let i = -9999;  // while block starts with command
            j = -8888;
        }
        let k = 0;
        if (~(i = 4321)) {
            do Output.printString("FAIL: i should be 4321, is ");
            do Output.printInt(i);
            do Output.println();
            let k = 1;
        }
        if (~(j = 5432)) {
            do Output.printString("FAIL: j should be 5432, is ");
            do Output.printInt(j);
            do Output.println();
            let k = 1;
        }
        if (k = 0) {
            do Output.printString("Test OK");
        }
        return;
    }
    function void test1() {
        var int i;      // function with vars starts with command
        return;
    }
    function void test2() {
        return;         // function without vars starts with command
    }
    function void test3(int i) {
        i = 0;          // function without vars starts with assignment
        return;
    }
}
	
	
	
	
				
			
		
	
	
		
		
			
				| 
					
	
	
	
	
				 | 
				
					
	
	 
		Cool =)
 The same approach can also be used to make the do keyword optional. All that's needed is a slight tweak to use lookahead in order to distinguish between an assignment and a call.
	
	
	
	 
				 | 
			
		
	 
	
		
		
			
				| 
					
	 Administrator 
	
	
	
				 | 
				
					
	
	
		Syntax changes including optional "do"
The book's syntax for  let and  do statements is:
 
 
    | statement: |   | 
    letStatement | ifStatement | whileStatement | doStatement | returnStatement |  
    | letStatement: |  | 
    'let' varName ( '[' expression ']' )? 
    '=' expression ';' |  
    | doStatement: |  | 
    'do' subroutineCall ';' |  
    | subroutineCall : |  | 
    subroutineName '(' expressionList ')' | ( className | varName) '.' subroutineName '(' expressionList ')'  |  
The new syntax is:.
 
 
    | statement: |   | 
    assignment ';' | subroutineCall ';' | letStatement | ifStatement | whileStatement | doStatement | returnStatement |  
    | letStatement: |  | 
    'let' assignment ';' |  
    | assignment: |  | 
    varName ( '[' expression ']' )? 
    '=' expression |  
 
Note that the second token in both assignment and subroutineCall is a symbol.  Examining this symbol is the lookahead that tungsten mentioned.
	
	
	
	
				
			
		  
	
	
		
	
	
                        
                    
                
             
             
				
			
			
					 
					
					
				 |