├── Jack OS API.pdf ├── Sample Jack Codes ├── HelloWorld │ └── Main.jack ├── Square │ ├── Main.jack │ ├── SquareGame.jack │ └── Square.jack ├── Fraction │ ├── Main.jack │ └── Fraction.jack ├── List │ ├── Main.jack │ └── List.jack └── Average │ └── Main.jack ├── Symbol.java ├── README.md ├── Compiler.java ├── LICENSE ├── Token.java ├── SymbolTable.java ├── JackClasses.java ├── Lexer.java └── Parser.java /Jack OS API.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangjs96/A-tutorial-compiler-written-in-Java/HEAD/Jack OS API.pdf -------------------------------------------------------------------------------- /Sample Jack Codes/HelloWorld/Main.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/HelloWorld/Main.jack 5 | 6 | /** Hello World program. */ 7 | class Main { 8 | function void main() { 9 | /* Prints some text using the standard library. */ 10 | do Output.printString("Hello world!"); 11 | do Output.println(); // New line 12 | return; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sample Jack Codes/Square/Main.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Square/Main.jack 5 | 6 | /** Initializes a new Square Dance game and starts running it. */ 7 | class Main { 8 | function void main() { 9 | var SquareGame game; 10 | let game = SquareGame.new(); 11 | do game.run(); 12 | do game.dispose(); 13 | return; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sample Jack Codes/Fraction/Main.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Fraction/Main.jack 5 | 6 | // Computes the sum of 2/3 and 1/5. 7 | class Main { 8 | function void main() { 9 | var Fraction a, b, c; 10 | let a = Fraction.new(2,3); 11 | let b = Fraction.new(1,5); 12 | let c = a.plus(b); // Computes c = a + b 13 | do c.print(); // Prints "13/15" 14 | return; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sample Jack Codes/List/Main.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/List/Main.jack 5 | 6 | /** Demonstrates the use of the List abstraction. */ 7 | class Main { 8 | function void main() { 9 | // Creates and uses the list (2,3,5). 10 | var List v; 11 | let v = List.new(5,null); 12 | let v = List.new(2,List.new(3,v)); 13 | do v.print(); // prints 2 3 5 14 | do v.dispose(); // disposes the list 15 | return; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Symbol.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | /** 4 | * 5 | * @author wangjs 6 | */ 7 | //This class is used to define the symbol 8 | public class Symbol { 9 | 10 | public enum SymbolType { 11 | var, function, method, constructor, field, Static, argument 12 | }; 13 | public SymbolType kind; 14 | public boolean initOrNot = false; 15 | public String lexeme; 16 | public String returnType; 17 | public String assignDataType; 18 | public String beAssignedDataType; 19 | public String memorySegment; 20 | public int numOfVar; 21 | public int offset; 22 | public ArrayList argumentsDataType = new ArrayList(); 23 | } 24 | -------------------------------------------------------------------------------- /Sample Jack Codes/Average/Main.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Average/Main.jack 5 | 6 | // Inputs some numbers and computes their average 7 | class Main { 8 | function void main() { 9 | var Array a; 10 | var int length; 11 | var int i, sum; 12 | 13 | let length = Keyboard.readInt("How many numbers? "); 14 | let a = Array.new(length); // constructs the array 15 | 16 | let i = 0; 17 | while (i < length) { 18 | let a[i] = Keyboard.readInt("Enter a number: "); 19 | let sum = sum + a[i]; 20 | let i = i + 1; 21 | } 22 | 23 | do Output.printString("The average is "); 24 | do Output.printInt(sum / length); 25 | return; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Instructions: 2 | 1. Download the Java source codes 3 | 2. Store these codes into a local folder and open this folder 4 | 3. Click the right key of mouse and click ‘Open in Terminal’ 5 | 4. Input command as ‘javac *.java’ to compiler all Java source codes to get the Java class files 6 | 5. Input command as ‘java Compiler FolderName’ (FolderName indicates the name of folder which contains the Jack source codes) 7 | 8 | For the step 5, if user wants to compile the folder which is not in the ‘src’ folder, the entire path of the folder should be added in front of the folder name. 9 | 10 | Jack programming language is a kind of tutorial object-oriented programming language. I have already uploaded several samples with documentation for Jack programming language. After generating the .vm files, users can download virtual machine from www.nand2tetris.org to run the machine codes. 11 | -------------------------------------------------------------------------------- /Compiler.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | 3 | /** 4 | * 5 | * @author wangjs 6 | */ 7 | //This is main class used to call other parts of the compiler 8 | public class Compiler { 9 | 10 | /** 11 | * @param args the command line arguments 12 | */ 13 | public static void main(String[] args) { 14 | //Read the files stored in the directory provided 15 | File files = new File(args[0]); 16 | File[] allFile = files.listFiles(); 17 | /*Traverse the files in the directory. 18 | Use lexer to produce the tokens and use parser to check the programming language grammar. 19 | Then check whether there are lots of sementic mistakes and produce the vm codes*/ 20 | for (File f : allFile) { 21 | if (f.isFile() && f.getName().contains(".jack")) { 22 | Lexer lexer = new Lexer(args[0], f.getName()); 23 | Parser parser = new Parser(lexer); 24 | } 25 | } 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 gooooooood 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sample Jack Codes/List/List.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/List/List.jack 5 | 6 | /** Represents a linked list of integers. */ 7 | class List { 8 | field int data; // a list consists of a data field, 9 | field List next; // followed by a list 10 | 11 | /* Creates a List. */ 12 | constructor List new(int car, List cdr) { 13 | let data = car; // the identifiers car and cdr are used in 14 | let next = cdr; // memory of the Lisp programming language 15 | return this; 16 | } 17 | 18 | /** Accessors. */ 19 | method int getData() { return data; } 20 | method int getNext() { return next; } 21 | 22 | /** Prints this list. */ 23 | method void print() { 24 | var List current; // initializes current to the first item 25 | let current = this; // of this list 26 | while (~(current = null)) { 27 | do Output.printInt(current.getData()); 28 | do Output.printChar(32); // prints a space 29 | let current = current.getNext(); 30 | } 31 | return; 32 | } 33 | 34 | /** Disposes this List by recursively disposing its tail. */ 35 | method void dispose() { 36 | if (~(next = null)) { 37 | do next.dispose(); 38 | } 39 | // Uses an OS routine to recycle this object. 40 | do Memory.deAlloc(this); 41 | return; 42 | } 43 | 44 | // More list processing methods can come here. 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Sample Jack Codes/Fraction/Fraction.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Fraction/Fraction.jack 5 | 6 | /** Represents the Fraction type and related operations. */ 7 | class Fraction { 8 | field int numerator, denominator; // field = property = member variable. 9 | 10 | /** Constructs a (reduced) fraction from the given numerator and denominator. */ 11 | constructor Fraction new(int x, int y) { 12 | let numerator = x; 13 | let denominator = y; 14 | do reduce(); // reduces the fraction 15 | return this; // a constructor is expected to return a reference to the new object 16 | } 17 | 18 | // Reduces this fraction. 19 | method void reduce() { 20 | var int g; 21 | let g = Fraction.gcd(numerator, denominator); 22 | if (g > 1) { 23 | let numerator = numerator / g; 24 | let denominator = denominator / g; 25 | } 26 | return; 27 | } 28 | 29 | /** Accessors. */ 30 | method int getNumerator() { return numerator; } 31 | method int getDenominator() { return denominator; } 32 | 33 | /** Returns the sum of this fraction and the other one. */ 34 | method Fraction plus(Fraction other) { 35 | var int sum; 36 | let sum = (numerator * other.getDenominator()) + (other.getNumerator() * denominator); 37 | return Fraction.new(sum, denominator * other.getDenominator()); 38 | } 39 | 40 | // More fraction-related methods (minus, times, div, etc.) can be added here. 41 | 42 | /** Disposes this fraction. */ 43 | method void dispose() { 44 | do Memory.deAlloc(this); // uses an OS routine to recycle the memory held by the object 45 | return; 46 | } 47 | 48 | /** Prints this fraction in the format x/y. */ 49 | method void print() { 50 | do Output.printInt(numerator); 51 | do Output.printString("/"); 52 | do Output.printInt(denominator); 53 | return; 54 | } 55 | 56 | // Computes the greatest common divisor of the given integers. 57 | function int gcd(int a, int b) { 58 | var int r; 59 | while (~(b = 0)) { // applies Euclid's algorithm 60 | let r = a - (b * (a / b)); // r = remainder of the integer division a/b 61 | let a = b; let b = r; 62 | } 63 | return a; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sample Jack Codes/Square/SquareGame.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Square/SquareGame.jack 5 | 6 | /** 7 | * Implements the Square Dance game. 8 | * This simple game allows the user to move a black square around 9 | * the screen, and change the square's size during the movement. 10 | * When the game starts, a square of 30 by 30 pixels is shown at the 11 | * top-left corner of the screen. The user controls the square as follows. 12 | * The 4 arrow keys are used to move the square up, down, left, and right. 13 | * The 'z' and 'x' keys are used, respectively, to decrement and increment 14 | * the square's size. The 'q' key is used to quit the game. 15 | */ 16 | 17 | class SquareGame { 18 | field Square square; // the square of this game 19 | field int direction; // the square's current direction: 20 | // 0=none, 1=up, 2=down, 3=left, 4=right 21 | 22 | /** Constructs a new Square Game. */ 23 | constructor SquareGame new() { 24 | // Creates a 30 by 30 pixels square and positions it at the top-left 25 | // of the screen. 26 | let square = Square.new(0, 0, 30); 27 | let direction = 0; // initial state is no movement 28 | return this; 29 | } 30 | 31 | /** Disposes this game. */ 32 | method void dispose() { 33 | do square.dispose(); 34 | do Memory.deAlloc(this); 35 | return; 36 | } 37 | 38 | /** Moves the square in the current direction. */ 39 | method void moveSquare() { 40 | if (direction = 1) { do square.moveUp(); } 41 | if (direction = 2) { do square.moveDown(); } 42 | if (direction = 3) { do square.moveLeft(); } 43 | if (direction = 4) { do square.moveRight(); } 44 | do Sys.wait(5); // delays the next movement 45 | return; 46 | } 47 | 48 | /** Runs the game: handles the user's inputs and moves the square accordingly */ 49 | method void run() { 50 | var char key; // the key currently pressed by the user 51 | var boolean exit; 52 | let exit = false; 53 | 54 | while (~exit) { 55 | // waits for a key to be pressed 56 | while (key = 0) { 57 | let key = Keyboard.keyPressed(); 58 | do moveSquare(); 59 | } 60 | if (key = 81) { let exit = true; } // q key 61 | if (key = 90) { do square.decSize(); } // z key 62 | if (key = 88) { do square.incSize(); } // x key 63 | if (key = 131) { let direction = 1; } // up arrow 64 | if (key = 133) { let direction = 2; } // down arrow 65 | if (key = 130) { let direction = 3; } // left arrow 66 | if (key = 132) { let direction = 4; } // right arrow 67 | 68 | // waits for the key to be released 69 | while (~(key = 0)) { 70 | let key = Keyboard.keyPressed(); 71 | do moveSquare(); 72 | } 73 | } // while 74 | return; 75 | } 76 | } 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Token.java: -------------------------------------------------------------------------------- 1 | import java.util.HashMap; 2 | import java.util.Map; 3 | /** 4 | * 5 | * @author wangjs 6 | */ 7 | public class Token { 8 | //Enum used to identify the type of tokens 9 | public enum TokenType{ID,Symbol,Keyword,EOF,Constant,Comment,String,Error,Null}; 10 | //Enum used to identify the specific operators 11 | public enum SymbolType{assignment,addition,multiplication,subtraction,division, 12 | equal,parentheses,braces,brackets,and,or,not,lower,larger,one_line_com, 13 | multi_line_com,API_com,com_e,semicolon,dot,quotation,dquotation,comma,line_break}; 14 | //Array used to store the keywords 15 | final public String[] Keywords = {"class","constructor","method","function","int", 16 | "boolean","char","void","var","static","field","let","do","if","else", 17 | "while","return","true","false","null","this"}; 18 | //Hashmap used to construct the map between the symbols and their usages 19 | final public Map Symbols = new HashMap(); 20 | 21 | public String Token = ""; 22 | public TokenType Type = null; 23 | public int LineNumber = 0; 24 | public SymbolType SymbolType = null; 25 | //Initialize the hashmap 26 | public Token(){ 27 | initSymbols(); 28 | } 29 | private void initSymbols(){ 30 | Symbols.put("(", SymbolType.parentheses); 31 | Symbols.put(")", SymbolType.parentheses); 32 | Symbols.put("{", SymbolType.braces); 33 | Symbols.put("}", SymbolType.braces); 34 | Symbols.put("[", SymbolType.brackets); 35 | Symbols.put("]", SymbolType.brackets); 36 | Symbols.put("=", SymbolType.assignment); 37 | Symbols.put("+", SymbolType.addition); 38 | Symbols.put("-", SymbolType.subtraction); 39 | Symbols.put("*", SymbolType.multiplication); 40 | Symbols.put("/", SymbolType.division); 41 | Symbols.put("&", SymbolType.and); 42 | Symbols.put("|", SymbolType.or); 43 | Symbols.put("~", SymbolType.not); 44 | Symbols.put(">", SymbolType.larger); 45 | Symbols.put("<", SymbolType.lower); 46 | Symbols.put("//", SymbolType.one_line_com); 47 | Symbols.put("/*", SymbolType.multi_line_com); 48 | Symbols.put("/**", SymbolType.API_com); 49 | Symbols.put("*/",SymbolType.com_e); 50 | Symbols.put("==", SymbolType.equal); 51 | Symbols.put(";", SymbolType.semicolon); 52 | Symbols.put(".",SymbolType.dot); 53 | Symbols.put("\"",SymbolType.dquotation); 54 | Symbols.put("'",SymbolType.quotation); 55 | Symbols.put(",",SymbolType.comma); 56 | Symbols.put("\\n",SymbolType.line_break); 57 | } 58 | //Set the values of the parameters of token object 59 | public void setToken(String Token,TokenType Type,int LineNumber){ 60 | this.Token = Token; 61 | this.Type = Type; 62 | this.LineNumber = LineNumber; 63 | } 64 | public void changeTokenContent(){ 65 | TokenType Type = null; 66 | this.Token = ""; 67 | this.Type = Type.Null; 68 | } 69 | //Print out the contents stored in the each token object 70 | public void print(){ 71 | if(!Token.equals("")&&Type != null) 72 | System.out.println("<"+Token+","+Type.toString()+","+LineNumber+">"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sample Jack Codes/Square/Square.jack: -------------------------------------------------------------------------------- 1 | // This file is part of www.nand2tetris.org 2 | // and the book "The Elements of Computing Systems" 3 | // by Nisan and Schocken, MIT Press. 4 | // File name: projects/09/Square/Square.jack 5 | 6 | /** Implements a graphical square. */ 7 | class Square { 8 | 9 | field int x, y; // screen location of the square's top-left corner 10 | field int size; // length of this square, in pixels 11 | 12 | /** Constructs a new square with a given location and size. */ 13 | constructor Square new(int Ax, int Ay, int Asize) { 14 | let x = Ax; 15 | let y = Ay; 16 | let size = Asize; 17 | do draw(); 18 | return this; 19 | } 20 | 21 | /** Disposes this square. */ 22 | method void dispose() { 23 | do Memory.deAlloc(this); 24 | return; 25 | } 26 | 27 | /** Draws the square on the screen. */ 28 | method void draw() { 29 | do Screen.setColor(true); 30 | do Screen.drawRectangle(x, y, x + size, y + size); 31 | return; 32 | } 33 | 34 | /** Erases the square from the screen. */ 35 | method void erase() { 36 | do Screen.setColor(false); 37 | do Screen.drawRectangle(x, y, x + size, y + size); 38 | return; 39 | } 40 | 41 | /** Increments the square size by 2 pixels. */ 42 | method void incSize() { 43 | if (((y + size) < 254) & ((x + size) < 510)) { 44 | do erase(); 45 | let size = size + 2; 46 | do draw(); 47 | } 48 | return; 49 | } 50 | 51 | /** Decrements the square size by 2 pixels. */ 52 | method void decSize() { 53 | if (size > 2) { 54 | do erase(); 55 | let size = size - 2; 56 | do draw(); 57 | } 58 | return; 59 | } 60 | 61 | /** Moves the square up by 2 pixels. */ 62 | method void moveUp() { 63 | if (y > 1) { 64 | do Screen.setColor(false); 65 | do Screen.drawRectangle(x, (y + size) - 1, x + size, y + size); 66 | let y = y - 2; 67 | do Screen.setColor(true); 68 | do Screen.drawRectangle(x, y, x + size, y + 1); 69 | } 70 | return; 71 | } 72 | 73 | /** Moves the square down by 2 pixels. */ 74 | method void moveDown() { 75 | if ((y + size) < 254) { 76 | do Screen.setColor(false); 77 | do Screen.drawRectangle(x, y, x + size, y + 1); 78 | let y = y + 2; 79 | do Screen.setColor(true); 80 | do Screen.drawRectangle(x, (y + size) - 1, x + size, y + size); 81 | } 82 | return; 83 | } 84 | 85 | /** Moves the square left by 2 pixels. */ 86 | method void moveLeft() { 87 | if (x > 1) { 88 | do Screen.setColor(false); 89 | do Screen.drawRectangle((x + size) - 1, y, x + size, y + size); 90 | let x = x - 2; 91 | do Screen.setColor(true); 92 | do Screen.drawRectangle(x, y, x + 1, y + size); 93 | } 94 | return; 95 | } 96 | 97 | /** Moves the square right by 2 pixels. */ 98 | method void moveRight() { 99 | if ((x + size) < 510) { 100 | do Screen.setColor(false); 101 | do Screen.drawRectangle(x, y, x + 1, y + size); 102 | let x = x + 2; 103 | do Screen.setColor(true); 104 | do Screen.drawRectangle((x + size) - 1, y, x + size, y + size); 105 | } 106 | return; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /SymbolTable.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | /** 4 | * 5 | * @author wangjs 6 | */ 7 | //This class is used to store the symbols and provide several interfaces for other classes 8 | public class SymbolTable { 9 | public ArrayList symbols; 10 | public String varName; 11 | public String memorySegment; 12 | public String dataType; 13 | public String functionType; 14 | public String returnType; 15 | public int offset; 16 | public int counter; 17 | public int numOfVar; 18 | public int numOfArgs; 19 | public boolean initOrNot; 20 | //Initialize each variable 21 | public SymbolTable() { 22 | symbols = new ArrayList(); 23 | counter = 0; 24 | varName = ""; 25 | memorySegment = ""; 26 | dataType = ""; 27 | functionType = ""; 28 | returnType = ""; 29 | offset = 0; 30 | numOfVar = 0; 31 | numOfArgs = 0; 32 | } 33 | //Add the symbol of identifier into the symbol table 34 | public void addIdentifierSymbolTable(String lexeme, Symbol.SymbolType kind, String assignDataType, String beAssignDataType, String memorySegment) { 35 | Symbol symbol = new Symbol(); 36 | symbol.lexeme = lexeme; 37 | symbol.kind = kind; 38 | symbol.offset = counter++; 39 | symbol.assignDataType = assignDataType; 40 | symbol.beAssignedDataType = beAssignDataType; 41 | symbol.memorySegment = memorySegment; 42 | symbols.add(symbol); 43 | } 44 | //Add the symbol of subroutine into the symbol table 45 | public void addFunctionSymbolTable(String lexeme, Symbol.SymbolType kind, ArrayList argumentsDataType, String returnType, int numOfVar) { 46 | Symbol symbol = new Symbol(); 47 | symbol.lexeme = lexeme; 48 | symbol.kind = kind; 49 | symbol.argumentsDataType = argumentsDataType; 50 | symbol.returnType = returnType; 51 | symbol.numOfVar = numOfVar; 52 | symbols.add(symbol); 53 | } 54 | //Store the names of subroutines in the current class 55 | public void addLocalFunctionName(String lexeme) { 56 | Symbol symbol = new Symbol(); 57 | symbol.lexeme = lexeme; 58 | } 59 | //Find if the lexeme required exists in the current symbol table for variable 60 | public boolean findIdentifierSymbol(String lexeme) { 61 | for (Symbol symbol : symbols) { 62 | if (symbol.lexeme.equals(lexeme)) { 63 | varName = symbol.lexeme; 64 | memorySegment = symbol.memorySegment; 65 | offset = symbol.offset; 66 | dataType = symbol.assignDataType; 67 | initOrNot = symbol.initOrNot; 68 | return true; 69 | } 70 | } 71 | return false; 72 | } 73 | //Find if the lexeme required exists in the current symbol table for subroutine 74 | public boolean findFunctionSymbol(String lexeme) { 75 | for (Symbol symbol : symbols) { 76 | if (symbol.lexeme.equals(lexeme)) { 77 | functionType = symbol.kind.toString(); 78 | returnType = symbol.returnType; 79 | numOfVar = symbol.numOfVar; 80 | return true; 81 | } 82 | } 83 | return false; 84 | } 85 | //Return the value of offset 86 | public int getOffset(String lexeme) { 87 | int offset = 0; 88 | for (Symbol symbol : symbols) { 89 | if (symbol.lexeme.equals(lexeme)) { 90 | offset = symbol.offset; 91 | break; 92 | } 93 | } 94 | return offset; 95 | } 96 | /*Check if the data type of current lexeme matches 97 | with the type transmitted into the method as the argument 98 | */ 99 | public boolean checkSymbolType(String lexeme, String type) { 100 | for (Symbol symbol : symbols) { 101 | if (symbol.lexeme.equals(lexeme)) { 102 | if (symbol.kind.toString().equals(type)) { 103 | return true; 104 | } 105 | } 106 | } 107 | return false; 108 | } 109 | //Return the arguments of the subroutine 110 | public String getArgument(String lexeme) { 111 | String arguments = ""; 112 | numOfArgs = 0; 113 | for (Symbol symbol : symbols) { 114 | if (symbol.lexeme.equals(lexeme)) { 115 | for (String argument : symbol.argumentsDataType) { 116 | numOfArgs++; 117 | arguments += (argument + " "); 118 | } 119 | } 120 | } 121 | return arguments; 122 | } 123 | //Return the data type of return value of subroutine 124 | public String getFunctionReturnType(String lexeme) { 125 | String returnType = ""; 126 | for (Symbol symbol : symbols) { 127 | if (symbol.lexeme.equals(lexeme)) { 128 | returnType = symbol.returnType; 129 | break; 130 | } 131 | } 132 | return returnType; 133 | } 134 | 135 | public String getIdentifierAssignDataType(String lexeme) { 136 | String dataType = ""; 137 | for (Symbol symbol : symbols) { 138 | if (symbol.lexeme.equals(lexeme)) { 139 | dataType = symbol.assignDataType; 140 | break; 141 | } 142 | } 143 | return dataType; 144 | } 145 | 146 | public String getIdentifierBeAssignedDataType(String lexeme) { 147 | String dataType = ""; 148 | for (Symbol symbol : symbols) { 149 | if (symbol.lexeme.equals(lexeme)) { 150 | dataType = symbol.beAssignedDataType; 151 | break; 152 | } 153 | } 154 | return dataType; 155 | } 156 | 157 | 158 | //Set the counter variable 159 | public void setCounter(int counter) { 160 | this.counter = counter; 161 | } 162 | //Set the state of initialization to true 163 | public void setInitOrNot(String lexeme) { 164 | for (Symbol symbol : symbols) { 165 | if (symbol.lexeme.equals(lexeme)) { 166 | symbol.initOrNot = true; 167 | break; 168 | } 169 | } 170 | } 171 | //Set the state of initialization to false 172 | public void setInitToNot(String lexeme) { 173 | for (Symbol symbol : symbols) { 174 | if (symbol.lexeme.equals(lexeme)) { 175 | symbol.initOrNot = false; 176 | break; 177 | } 178 | } 179 | } 180 | 181 | //Return the state of initialization 182 | public boolean getInitOrNot(String lexeme) { 183 | boolean initOrNot = false; 184 | for (Symbol symbol : symbols) { 185 | if (symbol.lexeme.equals(lexeme)) { 186 | initOrNot = symbol.initOrNot; 187 | break; 188 | } 189 | } 190 | return initOrNot; 191 | } 192 | //Print the current symbol table 193 | public void print() { 194 | for (Symbol symbol : symbols) { 195 | System.out.println(symbol.lexeme + "," + symbol.initOrNot); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /JackClasses.java: -------------------------------------------------------------------------------- 1 | import java.util.HashMap; 2 | import java.util.Map; 3 | 4 | /** 5 | * 6 | * @author wangjs 7 | */ 8 | //This class is used to store the information of Jack libraries 9 | public class JackClasses { 10 | 11 | final public String[] jackLibraries = {"int", 12 | "boolean", "char", "Math", "Array", "Memory", "Screen", "Keyboard", "Output", "String", "Sys"}; 13 | final public Map functionsArguments = new HashMap(); 14 | final public Map returnTypes = new HashMap(); 15 | final public Map dataTypeTransfer = new HashMap(); 16 | public String[] methods = {"Array.dispose", "String.dispose", "String.length", "String.charAt", 17 | "String.setCharAt", "String.appendChar", "String.eraseLastChar", "String.intValue", "String.setInt"}; 18 | 19 | public JackClasses() { 20 | initArgumentDictionary(); 21 | initReturnTypeDictionary(); 22 | dataTypeTransferDictionary(); 23 | } 24 | //Insert the arguments of Jack libraries into the dictionary 25 | private void initArgumentDictionary() { 26 | functionsArguments.put("Math.abs", "(int|all) "); 27 | functionsArguments.put("Math.multiply", "(int|all) (int|all) "); 28 | functionsArguments.put("Math.divide", "(int|all) (int|all) "); 29 | functionsArguments.put("Math.min", "(int|all) (int|all) "); 30 | functionsArguments.put("Math.max", "(int|all) (int|all) "); 31 | functionsArguments.put("Math.sqrt", "(int|all) "); 32 | 33 | functionsArguments.put("Array.new", "(int|all) "); 34 | functionsArguments.put("Array.dispose", ""); 35 | 36 | functionsArguments.put("Memory.peek", "(int|all) "); 37 | functionsArguments.put("Memory.poke", "(int|all) (int|all) "); 38 | functionsArguments.put("Memory.alloc", "(int|all) "); 39 | functionsArguments.put("Memory.deAlloc", "(Array|class|all) "); 40 | 41 | functionsArguments.put("Screen.clearScreen", ""); 42 | functionsArguments.put("Screen.setColor", "(int|boolean|all) "); 43 | functionsArguments.put("Screen.drawPixel", "(int|all) (int|all) (int|all) (int|all) "); 44 | functionsArguments.put("Screen.drawRectangle", "(int|all) (int|all) (int|all) (int|all) "); 45 | functionsArguments.put("Screen.drawLine", "(int|all) (int|all) (int|all) (int|all) "); 46 | functionsArguments.put("Screen.drawCircle", "(int|all) (int|all) (int|all) "); 47 | functionsArguments.put("Screen.clearCircle", "(int|all) (int|all) (int|all) "); 48 | 49 | 50 | functionsArguments.put("Keyboard.keyPressed", ""); 51 | functionsArguments.put("Keyboard.readChar", ""); 52 | functionsArguments.put("Keyboard.readLine", "(String|null|all) "); 53 | functionsArguments.put("Keyboard.readInt", "(String|null|all) "); 54 | 55 | functionsArguments.put("Output.init", ""); 56 | functionsArguments.put("Output.moveCursor", "(int|all) (int|all) "); 57 | functionsArguments.put("Output.printChar", "(int|char|all) "); 58 | functionsArguments.put("Output.printString", "(String|null|all) "); 59 | functionsArguments.put("Output.printInt", "(int|Array|char|all) "); 60 | functionsArguments.put("Output.println", ""); 61 | functionsArguments.put("Output.backSpace", ""); 62 | 63 | functionsArguments.put("String.new", "(int|all) "); 64 | functionsArguments.put("String.dispose", ""); 65 | functionsArguments.put("String.length", ""); 66 | functionsArguments.put("String.charAt", "(int|all) "); 67 | functionsArguments.put("String.setCharAt", "(int|all) (int|char|all) "); 68 | functionsArguments.put("String.appendChar", "(int|char|all) "); 69 | functionsArguments.put("String.eraseLastChar", ""); 70 | functionsArguments.put("String.intValue", ""); 71 | functionsArguments.put("String.setInt", "(int|all) "); 72 | functionsArguments.put("String.newLine", ""); 73 | functionsArguments.put("String.backSpace", ""); 74 | functionsArguments.put("String.doubleQuote", ""); 75 | 76 | functionsArguments.put("Sys.halt", ""); 77 | functionsArguments.put("Sys.error", "(int|all) "); 78 | functionsArguments.put("Sys.wait", "(int|all) "); 79 | } 80 | //Insert the return type of Jack libraries into the dictionary 81 | private void initReturnTypeDictionary() { 82 | returnTypes.put("Math.abs", "int"); 83 | returnTypes.put("Math.multiply", "int"); 84 | returnTypes.put("Math.divide", "int"); 85 | returnTypes.put("Math.min", "int"); 86 | returnTypes.put("Math.max", "int"); 87 | returnTypes.put("Math.sqrt", "int"); 88 | 89 | returnTypes.put("Array.new", "Array"); 90 | returnTypes.put("Array.dispose", "void"); 91 | 92 | returnTypes.put("Memory.peek", "int"); 93 | returnTypes.put("Memory.poke", "void"); 94 | returnTypes.put("Memory.alloc", "Array"); 95 | returnTypes.put("Memory.deAlloc", "void"); 96 | 97 | returnTypes.put("Screen.clearScreen", "void"); 98 | returnTypes.put("Screen.setColor", "void"); 99 | returnTypes.put("Screen.drawPixel", "void"); 100 | returnTypes.put("Screen.drawRectangle", "void"); 101 | returnTypes.put("Screen.drawLine", "void"); 102 | returnTypes.put("Screen.drawCircle", "void"); 103 | returnTypes.put("Screen.clearCircle", "void"); 104 | 105 | returnTypes.put("Keyboard.keyPressed", "char"); 106 | returnTypes.put("Keyboard.readChar", "char"); 107 | returnTypes.put("Keyboard.readLine", "String"); 108 | returnTypes.put("Keyboard.readInt", "int"); 109 | 110 | returnTypes.put("Output.init", "void"); 111 | returnTypes.put("Output.moveCursor", "void"); 112 | returnTypes.put("Output.printChar", "void"); 113 | returnTypes.put("Output.printString", "void"); 114 | returnTypes.put("Output.printInt", "void"); 115 | returnTypes.put("Output.println", "void"); 116 | returnTypes.put("Output.backSpace", "void"); 117 | 118 | returnTypes.put("String.new", "String"); 119 | returnTypes.put("String.dispose", "void"); 120 | returnTypes.put("String.length", "int"); 121 | returnTypes.put("String.charAt", "char"); 122 | returnTypes.put("String.setCharAt", "void"); 123 | returnTypes.put("String.appendChar", "String"); 124 | returnTypes.put("String.eraseLastChar", "void"); 125 | returnTypes.put("String.intValue", "int"); 126 | returnTypes.put("String.setInt", "void"); 127 | returnTypes.put("String.newLine", "char"); 128 | returnTypes.put("String.backSpace", "char"); 129 | returnTypes.put("String.doubleQuote", "char"); 130 | 131 | returnTypes.put("Sys.halt", "void"); 132 | returnTypes.put("Sys.error", "void"); 133 | returnTypes.put("Sys.wait", "void"); 134 | } 135 | //Store the information for type conversion into the dictionary 136 | private void dataTypeTransferDictionary() { 137 | dataTypeTransfer.put("char", "(char|int|null)"); 138 | dataTypeTransfer.put("String", "(String|null)"); 139 | dataTypeTransfer.put("int", "int|Array"); 140 | dataTypeTransfer.put("boolean", "(boolean|int)"); 141 | dataTypeTransfer.put("Array", "(Array|int|class|null)"); 142 | } 143 | //Check if the subroutine called is a method 144 | public boolean methodMatch(String methodName) { 145 | for (String method : methods) { 146 | if (method.equals(methodName)) { 147 | return true; 148 | } 149 | } 150 | return false; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Lexer.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.File; 3 | import java.io.FileInputStream; 4 | import java.io.InputStreamReader; 5 | import static java.lang.System.exit; 6 | import java.util.regex.Pattern; 7 | /** 8 | * 9 | * @author wangjs 10 | */ 11 | //This class is used to read each char and get the tokens 12 | public class Lexer { 13 | //Encoding format 14 | private String encoding; 15 | //File path 16 | private String path; 17 | //Directory path 18 | private static String folderPath; 19 | //File object 20 | private File file; 21 | //Variable used to store the line number 22 | private int lineNumber; 23 | //String variable used to store the text content stored in the source code file 24 | private String textContent; 25 | //Variable used to store the index used to record which characters have been accessed 26 | private int index; 27 | //Variable used to control whether the line number is needed to be increased 28 | public static boolean newLineCheck = true; 29 | //Constructor for initializing the lexer when parser needs to traverse the tokens from specific file 30 | public Lexer(String fileName) { 31 | encoding = "GBK"; 32 | path = folderPath + File.separator + fileName; 33 | file = new File(path); 34 | textContent = ""; 35 | lineNumber = 1; 36 | index = 0; 37 | } 38 | //Constructor for initializing the lexer when file is read from the specific directory 39 | public Lexer(String folderPath, String fileName) { 40 | encoding = "GBK"; 41 | this.folderPath = folderPath; 42 | path = folderPath + file.separator + fileName; 43 | file = new File(path); 44 | textContent = ""; 45 | lineNumber = 1; 46 | index = 0; 47 | } 48 | 49 | //Read the source code from the file into the program 50 | public void initLocalFile() { 51 | try { 52 | //String variable used to store the source codes 53 | textContent = ""; 54 | //Check whether the file read is available 55 | if (file.isFile() && file.exists()) { 56 | InputStreamReader read = new InputStreamReader( 57 | new FileInputStream(file), encoding); 58 | BufferedReader bufferedReader = new BufferedReader(read); 59 | String lineText = null; 60 | while ((lineText = bufferedReader.readLine()) != null) { 61 | textContent += (lineText + "\\n"); 62 | } 63 | read.close(); 64 | } else { 65 | System.out.println("Cannot find the file"); 66 | } 67 | } catch (Exception e) { 68 | System.out.println("Error: class not exists."); 69 | e.printStackTrace(); 70 | } 71 | } 72 | //Method used to get the folder path 73 | public String getFolderPath() { 74 | return folderPath; 75 | } 76 | //Change the index used to record the number of characters which have been read already 77 | public void setReadIndex(int value) { 78 | index = value; 79 | } 80 | //Method used to return the current index for recording the number of characters which have been read already 81 | public int getReadIndex() { 82 | return index; 83 | } 84 | //Method used to return the source codes 85 | public String getTextContent() { 86 | return textContent; 87 | } 88 | //Method used to create the error information when the format of comments are not suitable 89 | private void error(String errorInfor) { 90 | System.out.printf("%s", errorInfor); 91 | exit(0); 92 | } 93 | 94 | //Method used to operate the token exactly 95 | private Token TokenOperation(String operationType) { 96 | Token token = new Token(); 97 | String TokenName = ""; 98 | Pattern pattern = Pattern.compile("^[-\\+]?[\\d]+$"); 99 | while (index < textContent.length() - 2) { 100 | if (textContent.charAt(index) == ' ') { 101 | index++; 102 | continue; 103 | } else if (Character.isLowerCase(textContent.charAt(index)) 104 | || Character.isUpperCase(textContent.charAt(index)) 105 | || Character.isDigit(textContent.charAt(index))) { 106 | TokenName += textContent.charAt(index); 107 | index++; 108 | 109 | if (index < textContent.length() 110 | && !Character.isLowerCase(textContent.charAt(index)) 111 | && !Character.isUpperCase(textContent.charAt(index)) 112 | && !Character.isDigit(textContent.charAt(index))) { 113 | break; 114 | } 115 | } else { 116 | TokenName += textContent.charAt(index); 117 | TokenName = TokenName.trim(); 118 | index++; 119 | if (token.Symbols.containsKey(TokenName)) { 120 | if (TokenName.startsWith("/")) { 121 | if (TokenName.equals("/")) { 122 | if (Character.isLowerCase(textContent.charAt(index)) 123 | || Character.isUpperCase(textContent.charAt(index)) 124 | || Character.isDigit(textContent.charAt(index)) 125 | || textContent.charAt(index) == ' ') { 126 | break; 127 | } else { 128 | continue; 129 | } 130 | } else if (TokenName.equals("//") || TokenName.equals("/*")) { 131 | if (TokenName.equals("/*")) { 132 | if (textContent.indexOf("*/", index) == -1) { 133 | error("Error: comment doesn't have the end symbol, line: " + lineNumber); 134 | } 135 | String comment = textContent.substring(index, 136 | textContent.indexOf("*/", index)); 137 | String[] comment_s = comment.split("\\\\n"); 138 | lineNumber += comment_s.length - 1; 139 | 140 | TokenName += comment + "*/"; 141 | index = textContent.indexOf("*/", index) + 2; 142 | 143 | TokenName = ""; 144 | continue; 145 | } else { 146 | Token com_token = new Token(); 147 | String comment = textContent.substring(index, 148 | textContent.indexOf("\\n", index)); 149 | TokenName += comment; 150 | index = textContent.indexOf("\\n", index); 151 | TokenName = ""; 152 | continue; 153 | } 154 | } 155 | } else if (TokenName.startsWith("\"")) { 156 | Token string_token = new Token(); 157 | int i = textContent.indexOf("\"", index); 158 | if (i > 0) { 159 | TokenName += textContent.substring(index, i + 1); 160 | string_token.setToken(TokenName, 161 | string_token.Type.String, lineNumber); 162 | index = i + 1; 163 | TokenName = ""; 164 | return string_token; 165 | } 166 | break; 167 | } else { 168 | break; 169 | } 170 | } else { 171 | if (TokenName.startsWith("\\")) { 172 | if (textContent.charAt(index) == 'n') { 173 | if (newLineCheck && operationType.equals("get")) { 174 | lineNumber++; 175 | } 176 | TokenName = ""; 177 | index++; 178 | continue; 179 | } 180 | } 181 | } 182 | } 183 | } 184 | //Identify the type of the token and return the token 185 | if (!TokenName.equals("")) { 186 | TokenName = TokenName.trim(); 187 | 188 | while (true) { 189 | if (token.Symbols.containsKey(TokenName)) { 190 | 191 | token.setToken(TokenName, token.Type.Symbol, lineNumber); 192 | break; 193 | } else { 194 | for (String keyword : token.Keywords) { 195 | if (keyword.equals(TokenName)) { 196 | token.setToken(TokenName, token.Type.Keyword, lineNumber); 197 | } 198 | } 199 | if (token.Token != "") { 200 | break; 201 | } 202 | } 203 | if (pattern.matcher(TokenName).matches()) { 204 | token.setToken(TokenName, token.Type.Constant, lineNumber); 205 | break; 206 | } else { 207 | token.setToken(TokenName, token.Type.ID, lineNumber); 208 | break; 209 | } 210 | } 211 | } 212 | return token; 213 | } 214 | 215 | //Get the next token from the source code and move the index 216 | public Token GetNextToken() { 217 | return TokenOperation("get"); 218 | } 219 | 220 | //Peek the next token but not move the index 221 | public Token PeekNextToken() { 222 | int OldIndex = index; 223 | Token token = TokenOperation("peek"); 224 | index = OldIndex; 225 | return token; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /Parser.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedWriter; 2 | import java.io.File; 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import static java.lang.System.exit; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * 13 | * @author wangjs 14 | */ 15 | //This class is used to make the grammar checking, semantic analysis and code generation. 16 | //The symbol tables will also be created in this class 17 | public class Parser { 18 | //symbolTables[0] = classStaticVariableSymbolTable 19 | //symbolTables[1] = classFieldVariableSymbolTable 20 | //symbolTables[2] = functionVarSymbolTable 21 | //symbolTables[3] = functionArgumentSymbolTable 22 | //symbolTables[4] = otherClassFunctionsSymbolTable 23 | private Lexer lexer = null; 24 | private SymbolTable[] symbolTables = new SymbolTable[5]; 25 | private String textContent = null; 26 | private JackClasses jackClasses = null; 27 | private String Type = "int|char|boolean|ID|void|String|Array"; 28 | private String otherClassType = ""; 29 | private File vmFile = null; 30 | private String className = ""; 31 | private String expressionReturnType = ""; 32 | private String functionName = ""; 33 | private String subroutineKind = ""; 34 | private String returnType = ""; 35 | private String oldToken = ""; 36 | private String lastFunctionName = ""; 37 | private boolean arrayInitOrNot = false; 38 | private boolean constructorOrNot = false; 39 | private boolean methodOrNot = false; 40 | private int ifCounter = 0; 41 | private int whileCounter = 0; 42 | private int pushCounter = 0; 43 | private int eleNumberCounter = 0; 44 | private Map classFieldVar = new HashMap(); 45 | private Map classArrayNumOfEle = new HashMap(); 46 | private Map functionArrayNumOfEle = new HashMap(); 47 | 48 | //Initialize the lexer and each symboltable 49 | public Parser(Lexer lexer) { 50 | this.lexer = lexer; 51 | lexer.initLocalFile(); 52 | symbolTables[0] = new SymbolTable(); 53 | symbolTables[1] = new SymbolTable(); 54 | symbolTables[2] = new SymbolTable(); 55 | symbolTables[3] = new SymbolTable(); 56 | symbolTables[4] = new SymbolTable(); 57 | textContent = this.lexer.getTextContent(); 58 | jackClasses = new JackClasses(); 59 | parserAnalysis(); 60 | } 61 | //Create the VM file if it not exists currently 62 | private void vmFileCreate() { 63 | try { 64 | vmFile = new File(lexer.getFolderPath() + File.separator + className + ".vm"); 65 | if (!vmFile.exists()) { 66 | vmFile.createNewFile(); 67 | } 68 | 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | //Insert the VM codes into the VM file 74 | private void vmCodeInput(String vmCodes) { 75 | try { 76 | File log = new File(lexer.getFolderPath() + File.separator + className + ".vm"); 77 | FileWriter fileWriter = new FileWriter(log.getAbsoluteFile(), true); 78 | BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); 79 | bufferedWriter.write(vmCodes); 80 | bufferedWriter.close(); 81 | fileWriter.close(); 82 | } catch (IOException e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | //Output the error information and terminate the execution of the parser 87 | private void error(String errorInfor) { 88 | System.out.printf("%s\n", errorInfor); 89 | exit(0); 90 | } 91 | //Check if the class used in current Jack source codes exists in the local folder 92 | private boolean localFileCheck(String className) { 93 | try { 94 | className += ".jack"; 95 | File files = new File(lexer.getFolderPath()); 96 | File[] allFile = files.listFiles(); 97 | for (File f : allFile) { 98 | if (f.isFile()) { 99 | if (className.equals(f.getName())) { 100 | return true; 101 | } 102 | } 103 | } 104 | } catch (Exception e) { 105 | e.printStackTrace(); 106 | } 107 | return false; 108 | } 109 | //Clear the values of the temporary variables in the symbol tables 110 | private void symbolTableVarClear() { 111 | for (SymbolTable symbolTable : symbolTables) { 112 | symbolTable.memorySegment = ""; 113 | symbolTable.dataType = ""; 114 | symbolTable.returnType = ""; 115 | symbolTable.functionType = ""; 116 | symbolTable.numOfArgs = 0; 117 | symbolTable.numOfVar = 0; 118 | symbolTable.offset = 0; 119 | symbolTable.initOrNot = false; 120 | } 121 | } 122 | //Exact tokens from the lexical analyser and call the corresponding proecessing methods 123 | private void parserAnalysis() { 124 | try { 125 | 126 | while (lexer.getReadIndex() < textContent.length() - 2) { 127 | //Identify the token to call corresponding methods 128 | Token newToken = lexer.GetNextToken(); 129 | if (newToken.Token.equals("class")) { 130 | classCheck(); 131 | vmFileCreate(); 132 | ClassesFunctionsReference(); 133 | } else if (newToken.Token.equals("constructor") 134 | || newToken.Token.equals("function") 135 | || newToken.Token.equals("method")) { 136 | if (!functionName.equals(lastFunctionName)) { 137 | error("Error: in class: " + className + ", function \"" 138 | + functionName + "\" doesn't have return statement"); 139 | } 140 | if (newToken.Token.equals("method")) { 141 | methodOrNot = true; 142 | } else if (newToken.Token.equals("constructor")) { 143 | constructorOrNot = true; 144 | } 145 | subroutineKind = newToken.Token; 146 | functionCheck(); 147 | } else if (newToken.Token.equals("field")) { 148 | classVarDeclarCheck(); 149 | } else if (newToken.Token.equals("var")) { 150 | functionVarDeclarCheck(); 151 | } else if (newToken.Token.equals("let")) { 152 | letStatementCheck(); 153 | } else if (newToken.Token.equals("if")) { 154 | ifStatementCheck(); 155 | } else if (newToken.Token.equals("while")) { 156 | whileStatementCheck(); 157 | } else if (newToken.Token.equals("do")) { 158 | doStatementCheck(); 159 | } else if (newToken.Token.equals("return")) { 160 | lastFunctionName = functionName; 161 | returnStatementCheck(); 162 | } 163 | } 164 | if (!functionName.equals(lastFunctionName)) { 165 | error("Error: function \"" + functionName 166 | + "\" doesn't have return statement"); 167 | } 168 | } catch (Exception e) { 169 | e.printStackTrace(); 170 | } 171 | } 172 | //Check if the format of the class is correct 173 | private void classCheck() { 174 | Token lastToken = null; 175 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 176 | error("Error: in class: " + className + ", identifier is expected, line: " 177 | + lexer.PeekNextToken().LineNumber); 178 | } else { 179 | className = lexer.PeekNextToken().Token; 180 | Type += ("|" + className); 181 | otherClassType += className; 182 | lexer.GetNextToken(); 183 | } 184 | //Check the grammar of the source codes between the braces 185 | if (!lexer.PeekNextToken().Token.equals("{")) { 186 | error("Error: in class: " + className + ", \"{\" is expected, line: " 187 | + lexer.PeekNextToken().LineNumber); 188 | } else { 189 | lexer.GetNextToken(); 190 | } 191 | //Record the current index for recovering 192 | int oldIndex = lexer.getReadIndex(); 193 | Lexer.newLineCheck = false; 194 | //Get the last token 195 | while (lexer.getReadIndex() < textContent.length() - 2) { 196 | if (!lexer.PeekNextToken().Token.equals("")) { 197 | lastToken = lexer.PeekNextToken(); 198 | } 199 | lexer.GetNextToken(); 200 | 201 | } 202 | //Recover the index to the original value 203 | lexer.setReadIndex(oldIndex); 204 | Lexer.newLineCheck = true; 205 | //Check if the last token is right brace 206 | if (!lastToken.Token.equals("}")) { 207 | error("Error: in class: " + className + ", \"}\" is expected, line: " 208 | + lastToken.LineNumber); 209 | } 210 | 211 | } 212 | /*Declare a new lexer and exact each token from source codes to find all 213 | other classes used in current source codes. Then call the methods to search 214 | their variables and methods.*/ 215 | private void ClassesFunctionsReference() { 216 | Lexer classCheckLexer = new Lexer(className + ".jack"); 217 | classCheckLexer.initLocalFile(); 218 | //Read all tokens from source codes to find all kinds of variables 219 | while (classCheckLexer.getReadIndex() < classCheckLexer.getTextContent().length() - 2) { 220 | if (classCheckLexer.PeekNextToken().Token.equals("var") 221 | || classCheckLexer.PeekNextToken().Token.equals("field") 222 | || classCheckLexer.PeekNextToken().Token.equals("static")) { 223 | 224 | classCheckLexer.GetNextToken(); 225 | boolean jackLibrariesOrNot = false; 226 | for (String library : jackClasses.jackLibraries) { 227 | if (classCheckLexer.PeekNextToken().Token.equals(library)) { 228 | jackLibrariesOrNot = true; 229 | break; 230 | } 231 | } 232 | //If the data type is not pre-defined or Jack libraries 233 | if (!jackLibrariesOrNot) { 234 | Type += ("|" + classCheckLexer.PeekNextToken().Token); 235 | otherClassType += ("|" + classCheckLexer.PeekNextToken().Token); 236 | if (!classCheckLexer.PeekNextToken().Token.equals(className)) { 237 | classFunctionsCheck(classCheckLexer.PeekNextToken().Token); 238 | classStaticDeclarCheck(classCheckLexer.PeekNextToken().Token); 239 | } 240 | } 241 | } 242 | classCheckLexer.GetNextToken(); 243 | } 244 | classFunctionsCheck(className); 245 | classStaticDeclarCheck(className); 246 | } 247 | //This method is used to search and record all subroutines in the current class 248 | private void classFunctionsCheck(String name) { 249 | Pattern pattern = Pattern.compile(Type); 250 | ArrayList fieldVars = new ArrayList(); 251 | Token lastToken = null; 252 | String functionType = ""; 253 | String dataType = ""; 254 | //Declare a new lexer to get the tokens 255 | Lexer temLexer = new Lexer(name + ".jack"); 256 | temLexer.initLocalFile(); 257 | /*Construct a loop to supervise the value of index for terminating the loop 258 | when end of source codes have been reached.*/ 259 | while (temLexer.getReadIndex() < temLexer.getTextContent().length() - 2) { 260 | //Store all fields belonging to the current class 261 | if (temLexer.PeekNextToken().Token.equals("field")) { 262 | temLexer.GetNextToken(); 263 | 264 | if (!(pattern.matcher(temLexer.PeekNextToken().Token).matches() 265 | || localFileCheck(temLexer.PeekNextToken().Token))) { 266 | error("Error: in class: " + name + ", keyword is expected, line: " 267 | + temLexer.GetNextToken().LineNumber); 268 | } else { 269 | dataType = temLexer.PeekNextToken().Token; 270 | temLexer.GetNextToken(); 271 | } 272 | while (true) { 273 | 274 | if (!temLexer.PeekNextToken().Type.toString().equals("ID")) { 275 | error("Error: in class: " + name + ", identifier is expected, line: " 276 | + temLexer.PeekNextToken().LineNumber); 277 | } else { 278 | fieldVars.add(dataType); 279 | temLexer.GetNextToken(); 280 | } 281 | if (!temLexer.PeekNextToken().Token.equals(",")) { 282 | break; 283 | } else { 284 | temLexer.GetNextToken(); 285 | } 286 | } 287 | 288 | if (!temLexer.PeekNextToken().Token.equals(";")) { 289 | error("Error: in class: " + name + ", \";\" is expected, line: " 290 | + temLexer.PeekNextToken().LineNumber); 291 | } 292 | /*Store the information of all subroutines into the symbol table and 293 | check if the grammar of these subroutines are correct*/ 294 | } else if (temLexer.PeekNextToken().Token.equals("function") 295 | || temLexer.PeekNextToken().Token.equals("method") 296 | || temLexer.PeekNextToken().Token.equals("constructor")) { 297 | functionType = temLexer.PeekNextToken().Token; 298 | temLexer.GetNextToken(); 299 | int numOfVar = 0; 300 | String localFunctionName = ""; 301 | String localReturnType = ""; 302 | ArrayList argumentsDataType = new ArrayList(); 303 | if (!pattern.matcher(temLexer.PeekNextToken().Token).matches() 304 | && !pattern.matcher(temLexer.PeekNextToken().Type.toString()).matches()) { 305 | error("Error: in class: " + name + ", keyword is expected, line: " 306 | + temLexer.PeekNextToken().LineNumber); 307 | } else { 308 | localReturnType = temLexer.PeekNextToken().Token; 309 | temLexer.GetNextToken(); 310 | } 311 | 312 | if (!temLexer.PeekNextToken().Type.toString().equals("ID")) { 313 | error("Error: in class: " + name + ", identifier is expected, line: " 314 | + temLexer.PeekNextToken().LineNumber); 315 | } else { 316 | localFunctionName = temLexer.PeekNextToken().Token; 317 | temLexer.GetNextToken(); 318 | } 319 | //Check if the grammar in the argument list is correct 320 | if (!temLexer.PeekNextToken().Token.equals("(")) { 321 | error("Error: in class: " + name + ", \"(\" is expected, line: " 322 | + temLexer.GetNextToken().LineNumber); 323 | } else { 324 | temLexer.GetNextToken(); 325 | while (!temLexer.PeekNextToken().Token.equals(")")) { 326 | 327 | if (!pattern.matcher(temLexer.PeekNextToken().Token).matches() 328 | && !pattern.matcher(temLexer.PeekNextToken().Type.toString()).matches()) { 329 | error("Error: in class: " + name + ", keyword is expected, line: " 330 | + temLexer.PeekNextToken().LineNumber); 331 | } else { 332 | //Store the data type can be accepted for type conversion 333 | if (temLexer.PeekNextToken().Token.equals("Array")) { 334 | argumentsDataType.add("(int|Array|class|null|all)"); 335 | } else if (temLexer.PeekNextToken().Token.equals("String")) { 336 | argumentsDataType.add("(String|null|all)"); 337 | } else if (temLexer.PeekNextToken().Token.equals("char")) { 338 | argumentsDataType.add("(int|char|all)"); 339 | } else if (temLexer.PeekNextToken().Token.equals("boolean")) { 340 | argumentsDataType.add("(int|boolean|all)"); 341 | } else if (temLexer.PeekNextToken().Token.equals("int")) { 342 | argumentsDataType.add("(int|all)"); 343 | } else { 344 | argumentsDataType.add("(" + temLexer.PeekNextToken().Token + "|null|Array)"); 345 | } 346 | 347 | temLexer.GetNextToken(); 348 | } 349 | 350 | if (!temLexer.PeekNextToken().Type.toString().equals("ID")) { 351 | error("Error: in class: " + name + ", identifier is expected, line: " 352 | + temLexer.PeekNextToken().LineNumber); 353 | } else { 354 | temLexer.GetNextToken(); 355 | } 356 | 357 | if (!temLexer.PeekNextToken().Token.equals(",")) { 358 | break; 359 | } else { 360 | temLexer.GetNextToken(); 361 | } 362 | } 363 | if (!temLexer.PeekNextToken().Token.equals(")")) { 364 | error("Error: in class: " + name + ", \")\" is expected, line: " 365 | + temLexer.PeekNextToken().LineNumber); 366 | } else { 367 | temLexer.GetNextToken(); 368 | } 369 | } 370 | /*Check if the grammar of braces are correct and count the number 371 | of the local variables in the braces used in the code generation*/ 372 | if (!temLexer.PeekNextToken().Token.equals("{")) { 373 | error("Error: in class: " + name + ", \"{\" is expected, line: " 374 | + temLexer.PeekNextToken().LineNumber); 375 | } else { 376 | 377 | int counterLB = 0; 378 | int counterRB = 0; 379 | int oldIndex = 0; 380 | int result = 0; 381 | counterLB++; 382 | 383 | temLexer.GetNextToken(); 384 | oldIndex = temLexer.getReadIndex(); 385 | temLexer.newLineCheck = false; 386 | //Count the number of local variables 387 | while (temLexer.getReadIndex() < temLexer.getTextContent().length() - 2) { 388 | if (temLexer.PeekNextToken().Token.equals("var")) { 389 | temLexer.GetNextToken(); 390 | 391 | if (!pattern.matcher(temLexer.PeekNextToken().Token).matches()) { 392 | error("Error: in class: " + name + ", keyword is expected, line: " 393 | + temLexer.PeekNextToken().LineNumber); 394 | } else { 395 | temLexer.GetNextToken(); 396 | } 397 | while (true) { 398 | if (!temLexer.PeekNextToken().Type.toString().equals("ID")) { 399 | error("Error: in class: " + name + ", identifier is expected, line: " 400 | + temLexer.PeekNextToken().LineNumber); 401 | } else { 402 | numOfVar++; 403 | temLexer.GetNextToken(); 404 | } 405 | if (!temLexer.PeekNextToken().Token.equals(",")) { 406 | break; 407 | } else { 408 | temLexer.GetNextToken(); 409 | } 410 | } 411 | 412 | if (!temLexer.PeekNextToken().Token.equals(";")) { 413 | error("Error: in class: " + name + ", \";\" is expected, line: " 414 | + temLexer.PeekNextToken().LineNumber); 415 | } 416 | } 417 | 418 | if (temLexer.PeekNextToken().Token.equals("{")) { 419 | counterLB++; 420 | } else if (temLexer.PeekNextToken().Token.equals("}")) { 421 | counterRB++; 422 | } 423 | lastToken = temLexer.GetNextToken(); 424 | 425 | if (counterLB == counterRB) { 426 | result = 1; 427 | break; 428 | } 429 | } 430 | if (result == 0) { 431 | error("Error: in class: " + name + ", \"}\" is expected, line: " 432 | + temLexer.PeekNextToken().LineNumber); 433 | } 434 | 435 | temLexer.setReadIndex(oldIndex); 436 | temLexer.newLineCheck = true; 437 | } 438 | //Store the information of subroutines into the symbol table 439 | if (symbolTables[4].findFunctionSymbol(name + "." + localFunctionName)) { 440 | error("Error: in class: " + name + ", function redeclaration, line:" 441 | + temLexer.PeekNextToken().LineNumber); 442 | } else { 443 | if (functionType.equals("function")) { 444 | symbolTables[4].addFunctionSymbolTable(name + "." 445 | + localFunctionName, Symbol.SymbolType.function, 446 | argumentsDataType, localReturnType, numOfVar); 447 | } else if (functionType.equals("method")) { 448 | symbolTables[4].addFunctionSymbolTable(name + "." 449 | + localFunctionName, Symbol.SymbolType.method, 450 | argumentsDataType, localReturnType, numOfVar); 451 | } else { 452 | symbolTables[4].addFunctionSymbolTable(name + "." 453 | + localFunctionName, Symbol.SymbolType.constructor, 454 | argumentsDataType, localReturnType, numOfVar); 455 | } 456 | } 457 | } 458 | temLexer.GetNextToken(); 459 | } 460 | //Store the arraylist used to store the field variables into a dictionary 461 | classFieldVar.put(name, fieldVars); 462 | } 463 | //This method is used to check if the grammar of subroutine in current class is correct 464 | private void functionCheck() throws Exception { 465 | symbolTables[2].setCounter(0); 466 | symbolTables[3].setCounter(0); 467 | symbolTables[2].symbols.clear(); 468 | symbolTables[3].symbols.clear(); 469 | functionArrayNumOfEle.clear(); 470 | ifCounter = 0; 471 | whileCounter = 0; 472 | //If current subroutine is a method, the first argument should be 'this' 473 | if (methodOrNot) { 474 | symbolTables[3].addIdentifierSymbolTable("this", 475 | Symbol.SymbolType.argument, className, className, "this"); 476 | } 477 | //Check the grammar of the definition of subroutine 478 | Pattern pattern = Pattern.compile(Type); 479 | if (!pattern.matcher(lexer.PeekNextToken().Token).matches() 480 | && !pattern.matcher(lexer.PeekNextToken().Type.toString()).matches()) { 481 | error("Error: in class: " + className + ", keyword is expected, line: " 482 | + lexer.PeekNextToken().LineNumber); 483 | } else { 484 | 485 | returnType = lexer.PeekNextToken().Token; 486 | lexer.GetNextToken(); 487 | } 488 | 489 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 490 | error("Error: in class: " + className + ", identifier is expected, line: " 491 | + lexer.PeekNextToken().LineNumber); 492 | } else { 493 | functionName = lexer.PeekNextToken().Token; 494 | lexer.GetNextToken(); 495 | } 496 | //Check the grammar of the argument list 497 | if (!lexer.PeekNextToken().Token.equals("(")) { 498 | error("Error: in class: " + className + ", \"(\" is expected, line: " 499 | + lexer.PeekNextToken().LineNumber); 500 | } else { 501 | lexer.GetNextToken(); 502 | while (!lexer.PeekNextToken().Token.equals(")")) { 503 | String assignArgumentType = ""; 504 | String beAssignedArgumentType = ""; 505 | if (!pattern.matcher(lexer.PeekNextToken().Token).matches() 506 | && !pattern.matcher(lexer.PeekNextToken().Type.toString()).matches()) { 507 | error("Error: in class: " + className + ", keyword is expected, line: " 508 | + lexer.PeekNextToken().LineNumber); 509 | } else { 510 | //Store all the data types can be accepted for type conversion 511 | assignArgumentType = lexer.PeekNextToken().Token; 512 | if (assignArgumentType.equals("Array")) { 513 | beAssignedArgumentType = "(int|Array)"; 514 | } else if (assignArgumentType.equals("String")) { 515 | beAssignedArgumentType = "(String|null)"; 516 | } else if (assignArgumentType.equals("char")) { 517 | beAssignedArgumentType = "(int|char)"; 518 | } else if (assignArgumentType.equals("boolean")) { 519 | beAssignedArgumentType = "(int|boolean)"; 520 | } else if (assignArgumentType.equals("int")) { 521 | beAssignedArgumentType = assignArgumentType; 522 | } else { 523 | beAssignedArgumentType = "(" + assignArgumentType + "|null|Array)"; 524 | } 525 | lexer.GetNextToken(); 526 | } 527 | 528 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 529 | error("Error: in class: " + className + ", identifier is expected, line: " 530 | + lexer.PeekNextToken().LineNumber); 531 | } else { 532 | if (symbolTables[3].findIdentifierSymbol(lexer.PeekNextToken().Token)) { 533 | error("Error: in class: " + className + ", argument redeclaration, line:" 534 | + lexer.PeekNextToken().LineNumber); 535 | } else { 536 | symbolTables[3].addIdentifierSymbolTable(lexer.PeekNextToken().Token, 537 | Symbol.SymbolType.argument, assignArgumentType, beAssignedArgumentType, "argument"); 538 | } 539 | lexer.GetNextToken(); 540 | } 541 | 542 | if (!lexer.PeekNextToken().Token.equals(",")) { 543 | break; 544 | } else { 545 | lexer.GetNextToken(); 546 | } 547 | } 548 | if (!lexer.PeekNextToken().Token.equals(")")) { 549 | error("Error: in class: " + className + ", \")\" is expected, line: " 550 | + lexer.PeekNextToken().LineNumber); 551 | } else { 552 | lexer.GetNextToken(); 553 | } 554 | } 555 | oldToken = lexer.PeekNextToken().Token; 556 | symbolTables[4].findFunctionSymbol(className + "." + functionName); 557 | //Insert the VM codes of subroutine into the VM file 558 | vmCodeInput("function " + className + "." + functionName 559 | + " " + symbolTables[4].numOfVar + "\n"); 560 | if (constructorOrNot) { 561 | ArrayList fieldVarArray = (ArrayList) classFieldVar.get(className); 562 | vmCodeInput("push constant " + fieldVarArray.size() + "\n"); 563 | vmCodeInput("call Memory.alloc 1\n"); 564 | vmCodeInput("pop pointer 0\n"); 565 | constructorOrNot = false; 566 | } else if (methodOrNot) { 567 | vmCodeInput("push argument 0\n"); 568 | vmCodeInput("pop pointer 0\n"); 569 | methodOrNot = false; 570 | } 571 | //Reset the values of temporary variables in the symbol tables 572 | symbolTableVarClear(); 573 | } 574 | //This method is used to find all static belonging to current classes 575 | private void classStaticDeclarCheck(String name) { 576 | try { 577 | Pattern pattern = Pattern.compile(Type); 578 | //Declare the new lexical analyser 579 | Lexer temLexer = new Lexer(name + ".jack"); 580 | temLexer.initLocalFile(); 581 | /*Construct a loop to supervise the value of index for terminating the loop 582 | when end of source codes have been reached.*/ 583 | while (temLexer.getReadIndex() < temLexer.getTextContent().length() - 2) { 584 | if (temLexer.PeekNextToken().Token.equals("static")) { 585 | temLexer.GetNextToken(); 586 | String assignArgumentType = ""; 587 | String beAssignedArgumentType = ""; 588 | if (!pattern.matcher(temLexer.PeekNextToken().Token).matches() 589 | && !pattern.matcher(temLexer.PeekNextToken().Type.toString()).matches()) { 590 | error("Error: in class: " + name + ", keyword is expected, line: " + temLexer.PeekNextToken().LineNumber); 591 | } else { 592 | //Store all data types can be accepted for type conversion 593 | assignArgumentType = temLexer.PeekNextToken().Token; 594 | if (assignArgumentType.equals("Array")) { 595 | beAssignedArgumentType = "(int|Array|null|class|all)"; 596 | } else if (assignArgumentType.equals("String")) { 597 | beAssignedArgumentType = "(String|null|all)"; 598 | } else if (assignArgumentType.equals("char")) { 599 | beAssignedArgumentType = "(int|char|all)"; 600 | } else if (assignArgumentType.equals("boolean")) { 601 | beAssignedArgumentType = "(int|boolean|all)"; 602 | } else if (assignArgumentType.equals("int")) { 603 | beAssignedArgumentType = "(int|all)"; 604 | } else { 605 | beAssignedArgumentType = "(" + assignArgumentType + "|null|Array)"; 606 | } 607 | temLexer.GetNextToken(); 608 | } 609 | //Store the information of static variables into the symbol table 610 | while (true) { 611 | if (!temLexer.PeekNextToken().Type.toString().equals("ID")) { 612 | error("Error: in class: " + name + ", identifier is expected, line: " + temLexer.PeekNextToken().LineNumber); 613 | } else { 614 | if (symbolTables[0].findIdentifierSymbol(name + "." + temLexer.PeekNextToken().Token) 615 | && symbolTables[1].findIdentifierSymbol(temLexer.PeekNextToken().Token)) { 616 | error("Error: in class: " + className + ", Variable redeclaration, line: " + temLexer.PeekNextToken().LineNumber); 617 | } else { 618 | symbolTables[0].addIdentifierSymbolTable(name + "." + temLexer.PeekNextToken().Token, Symbol.SymbolType.Static, assignArgumentType, beAssignedArgumentType, "static"); 619 | if (assignArgumentType.equals("Array")) { 620 | classArrayNumOfEle.put(temLexer.PeekNextToken().Token, null); 621 | } 622 | } 623 | 624 | temLexer.GetNextToken(); 625 | } 626 | if (!temLexer.PeekNextToken().Token.equals(",")) { 627 | break; 628 | } else { 629 | temLexer.GetNextToken(); 630 | } 631 | } 632 | 633 | if (!temLexer.PeekNextToken().Token.equals(";")) { 634 | error("Error: in class: " + name + ", \";\" is expected, line: " + temLexer.PeekNextToken().LineNumber); 635 | } 636 | } 637 | temLexer.GetNextToken(); 638 | } 639 | 640 | } catch (Exception e) { 641 | e.printStackTrace(); 642 | } 643 | //Clear the values of temporary variables 644 | symbolTableVarClear(); 645 | } 646 | /*This method is used to check the grammar of the fields in current source 647 | codes and store the information of them into the corresponding symbol table 648 | */ 649 | private void classVarDeclarCheck() { 650 | Pattern pattern = Pattern.compile(Type); 651 | String assignArgumentType = ""; 652 | String beAssignedArgumentType = ""; 653 | if (!pattern.matcher(lexer.PeekNextToken().Token).matches() 654 | && !pattern.matcher(lexer.PeekNextToken().Type.toString()).matches()) { 655 | error("Error: in class: " + className + ", keyword is expected, line: " + lexer.PeekNextToken().LineNumber); 656 | } else { 657 | //Store all data types can be accepted for type conversion 658 | assignArgumentType = lexer.PeekNextToken().Token; 659 | if (assignArgumentType.equals("Array")) { 660 | beAssignedArgumentType = "(int|Array|all)"; 661 | } else if (assignArgumentType.equals("String")) { 662 | beAssignedArgumentType = "(String|null|all)"; 663 | } else if (assignArgumentType.equals("char")) { 664 | beAssignedArgumentType = "(int|char|all)"; 665 | } else if (assignArgumentType.equals("boolean")) { 666 | beAssignedArgumentType = "(int|boolean|all)"; 667 | } else if (assignArgumentType.equals("int")) { 668 | beAssignedArgumentType = "(int|all)"; 669 | } else { 670 | beAssignedArgumentType = "(" + assignArgumentType + "|null|Array)"; 671 | } 672 | 673 | lexer.GetNextToken(); 674 | } 675 | //Store the information of field variables into the symbol table 676 | while (true) { 677 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 678 | error("Error: in class: " + className + ", identifier is expected, line: " + lexer.PeekNextToken().LineNumber); 679 | } else { 680 | 681 | if (symbolTables[1].findIdentifierSymbol(lexer.PeekNextToken().Token) 682 | && symbolTables[0].findIdentifierSymbol(className + "." + lexer.PeekNextToken().Token)) { 683 | error("Error: in class: " + className + ", variable redeclaration, line: " + lexer.PeekNextToken().LineNumber); 684 | } else { 685 | symbolTables[1].addIdentifierSymbolTable(lexer.PeekNextToken().Token, Symbol.SymbolType.field, assignArgumentType, beAssignedArgumentType, "this"); 686 | if (assignArgumentType.equals("Array")) { 687 | classArrayNumOfEle.put(lexer.PeekNextToken().Token, null); 688 | } 689 | } 690 | 691 | lexer.GetNextToken(); 692 | } 693 | if (!lexer.PeekNextToken().Token.equals(",")) { 694 | break; 695 | } else { 696 | lexer.GetNextToken(); 697 | } 698 | } 699 | 700 | if (!lexer.PeekNextToken().Token.equals(";")) { 701 | error("Error: in class: " + className + ", \";\" is expected, line: " + lexer.PeekNextToken().LineNumber); 702 | } else { 703 | lexer.GetNextToken(); 704 | } 705 | //Clear the values of temporary variables in the symbol table 706 | symbolTableVarClear(); 707 | } 708 | /*This method is used to check the grammar of the local variables in the subroutine 709 | and insert the information of these variables into the symbol table 710 | */ 711 | private void functionVarDeclarCheck() { 712 | Pattern pattern = Pattern.compile(Type); 713 | String assignArgumentType = ""; 714 | String beAssignedArgumentType = ""; 715 | if (!pattern.matcher(lexer.PeekNextToken().Token).matches()) { 716 | error("Error: in class: " + className + ", keyword is expected, line: " + lexer.PeekNextToken().LineNumber); 717 | } else { 718 | //Store all data types can be accepted for type conversion 719 | assignArgumentType = lexer.PeekNextToken().Token; 720 | if (assignArgumentType.equals("Array")) { 721 | beAssignedArgumentType = "(int|Array|null|all)"; 722 | } else if (assignArgumentType.equals("String")) { 723 | beAssignedArgumentType = "(String|null|all)"; 724 | } else if (assignArgumentType.equals("char")) { 725 | beAssignedArgumentType = "(int|char|all)"; 726 | } else if (assignArgumentType.equals("boolean")) { 727 | beAssignedArgumentType = "(int|boolean|all)"; 728 | } else if (assignArgumentType.equals("int")) { 729 | beAssignedArgumentType = "(int|all)"; 730 | } else { 731 | beAssignedArgumentType = "(" + assignArgumentType + "|null|Array)"; 732 | } 733 | 734 | lexer.GetNextToken(); 735 | } 736 | //Store the information of local variables into the symbol table 737 | while (true) { 738 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 739 | error("Error: in class: " + className + ", identifier is expected, line: " + lexer.PeekNextToken().LineNumber); 740 | } else { 741 | if (symbolTables[3].findIdentifierSymbol(lexer.PeekNextToken().Token) 742 | || symbolTables[2].findIdentifierSymbol(lexer.PeekNextToken().Token)) { 743 | error("Error: in class: " + className + ", variable redeclaration, line: " + lexer.PeekNextToken().LineNumber); 744 | } else { 745 | symbolTables[2].addIdentifierSymbolTable(lexer.PeekNextToken().Token, Symbol.SymbolType.var, assignArgumentType, beAssignedArgumentType, "local"); 746 | if (assignArgumentType.equals("Array")) { 747 | functionArrayNumOfEle.put(lexer.PeekNextToken().Token, null); 748 | } 749 | } 750 | 751 | lexer.GetNextToken(); 752 | } 753 | 754 | if (!lexer.PeekNextToken().Token.equals(",")) { 755 | break; 756 | } else { 757 | lexer.GetNextToken(); 758 | } 759 | } 760 | if (!lexer.PeekNextToken().Token.equals(";")) { 761 | error("Error: in class: " + className + ", \";\" is expected, line: " + lexer.PeekNextToken().LineNumber); 762 | } else { 763 | oldToken = lexer.PeekNextToken().Token; 764 | lexer.GetNextToken(); 765 | } 766 | //Clear the values of the temporary variables in the symbol table 767 | symbolTableVarClear(); 768 | } 769 | 770 | /*This method is used to check the grammar of the let statement, store 771 | variable has been initialized and make the data type checking for the 772 | two sides around the equal sign. 773 | */ 774 | private void letStatementCheck() throws Exception { 775 | String lastToken = ""; 776 | String varName = ""; 777 | String memorySegment = ""; 778 | String dataType = ""; 779 | int offset = 0; 780 | boolean arrayOrNot = false; 781 | Pattern pattern = Pattern.compile(otherClassType); 782 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 783 | error("Error: in class: " + className 784 | + ", identifier is expected, line: " 785 | + lexer.PeekNextToken().LineNumber); 786 | } else { 787 | //Check if the variable is defined before using 788 | if (!(symbolTables[2].findIdentifierSymbol(lexer.PeekNextToken().Token) 789 | || symbolTables[3].findIdentifierSymbol(lexer.PeekNextToken().Token) 790 | || symbolTables[1].findIdentifierSymbol(lexer.PeekNextToken().Token) 791 | || symbolTables[0].findIdentifierSymbol(className + "." + lexer.PeekNextToken().Token))) { 792 | error("Error: in class: " + className + ", variable \"" 793 | + lexer.PeekNextToken().Token + "\" is not declared before, line: " 794 | + lexer.PeekNextToken().LineNumber); 795 | } 796 | 797 | for (SymbolTable symbolTable : symbolTables) { 798 | if (!symbolTable.dataType.equals("") 799 | && !symbolTable.memorySegment.equals("")) { 800 | varName = symbolTable.varName; 801 | offset = symbolTable.offset; 802 | dataType = symbolTable.dataType; 803 | memorySegment = symbolTable.memorySegment; 804 | break; 805 | } 806 | symbolTable.memorySegment = ""; 807 | symbolTable.dataType = ""; 808 | symbolTable.offset = 0; 809 | } 810 | 811 | lastToken = lexer.PeekNextToken().Token; 812 | lexer.GetNextToken(); 813 | 814 | } 815 | //Check if the variable on the left-hand side is an array 816 | if (lexer.PeekNextToken().Token.equals("[")) { 817 | lexer.GetNextToken(); 818 | expression(); 819 | vmCodeInput("push " + memorySegment + " " + offset + "\n"); 820 | if (!(expressionReturnType.equals("int") 821 | || expressionReturnType.equals("all"))) { 822 | error("Error: in class: " + className 823 | + ", \"int value\" is expected, line: " 824 | + lexer.PeekNextToken().LineNumber); 825 | } 826 | if (!lexer.PeekNextToken().Token.equals("]")) { 827 | error("Error: in class: " + className 828 | + ", \"]\" is expected, line: " 829 | + lexer.PeekNextToken().LineNumber); 830 | } else { 831 | if (!dataType.equals("Array")) { 832 | error("Error: in class: " + className 833 | + ", variable \"" + lastToken + "\" is not an array variable, line: " 834 | + lexer.PeekNextToken().LineNumber); 835 | } 836 | vmCodeInput("add\n"); 837 | arrayOrNot = true; 838 | lexer.GetNextToken(); 839 | } 840 | } 841 | if (!lexer.PeekNextToken().Token.equals("=")) { 842 | error("Error: in class: " + className 843 | + ", \"=\" is expected, line: " 844 | + lexer.PeekNextToken().LineNumber); 845 | } else { 846 | lexer.GetNextToken(); 847 | 848 | String arrayVarName = lexer.PeekNextToken().Token; 849 | expression(); 850 | //Check if it is the initialization for array and record the number of elements defined, 851 | if (dataType.equals("Array") && arrayInitOrNot) { 852 | if (functionArrayNumOfEle.containsKey(varName)) { 853 | functionArrayNumOfEle.replace(varName, eleNumberCounter); 854 | } else { 855 | classArrayNumOfEle.replace(varName, eleNumberCounter); 856 | } 857 | arrayInitOrNot = false; 858 | } 859 | //Insert the VM codes if the expression on the right-hand side is an array 860 | if (arrayOrNot) { 861 | vmCodeInput("pop temp 0\n"); 862 | vmCodeInput("pop pointer 1\n"); 863 | vmCodeInput("push temp 0\n"); 864 | vmCodeInput("pop that 0\n"); 865 | 866 | } else { 867 | //Check if the data type of two sides match with each other 868 | vmCodeInput("pop " + memorySegment + " " + offset + "\n"); 869 | if (Pattern.compile(symbolTables[2]. 870 | getIdentifierBeAssignedDataType(lastToken)). 871 | matcher(expressionReturnType).matches() 872 | || Pattern.compile(symbolTables[3]. 873 | getIdentifierBeAssignedDataType(lastToken)). 874 | matcher(expressionReturnType).matches() 875 | || Pattern.compile(symbolTables[1]. 876 | getIdentifierBeAssignedDataType(lastToken)). 877 | matcher(expressionReturnType).matches() 878 | || Pattern.compile(symbolTables[0]. 879 | getIdentifierBeAssignedDataType(className + "." + lastToken)). 880 | matcher(expressionReturnType).matches()) { 881 | if (((pattern.matcher(symbolTables[2]. 882 | getIdentifierAssignDataType(lastToken)).matches() 883 | || pattern.matcher(symbolTables[3]. 884 | getIdentifierAssignDataType(lastToken)).matches() 885 | || pattern.matcher(symbolTables[1]. 886 | getIdentifierAssignDataType(lastToken)).matches() 887 | || pattern.matcher(symbolTables[0]. 888 | getIdentifierAssignDataType(className + "." + lastToken)).matches()) 889 | && expressionReturnType.equals("Array"))) { 890 | if (!(functionArrayNumOfEle.containsKey(arrayVarName) 891 | && (((ArrayList) classFieldVar.get(dataType)).size() 892 | == (int) functionArrayNumOfEle.get(arrayVarName)))) { 893 | if (!(classArrayNumOfEle.containsKey(arrayVarName) 894 | && (((ArrayList) classFieldVar.get(dataType)).size() 895 | == (int) classArrayNumOfEle.get(arrayVarName)))) { 896 | error("Error: in class: " + className 897 | + ", the numer of fields not matches with the number of elements in the array, line: " 898 | + lexer.PeekNextToken().Token); 899 | } 900 | } 901 | } 902 | } else { 903 | error("Error: in class: " + className + ", data type of return acvalue is wrong, line " 904 | + lexer.PeekNextToken().LineNumber); 905 | } 906 | 907 | } 908 | if (!lexer.PeekNextToken().Token.equals(";")) { 909 | error("Error: in class: " + className + ", \";\" is expected, line: " 910 | + lexer.PeekNextToken().LineNumber); 911 | } else { 912 | //Mark if the variable on the left-hand side is initialized 913 | if (memorySegment.equals("local")) { 914 | symbolTables[2].setInitOrNot(lastToken); 915 | } else if (memorySegment.equals("argument")) { 916 | symbolTables[3].setInitOrNot(lastToken); 917 | } else if (memorySegment.equals("this")) { 918 | symbolTables[1].setInitOrNot(lastToken); 919 | } else { 920 | symbolTables[0].setInitOrNot(className + "." + lastToken); 921 | } 922 | oldToken = lexer.PeekNextToken().Token; 923 | lexer.GetNextToken(); 924 | } 925 | } 926 | symbolTableVarClear(); 927 | } 928 | //This method is used to check the grammar of the if statement and insert VM codes for if statement 929 | private void ifStatementCheck() throws Exception { 930 | boolean ifReturnOrNot = false; 931 | boolean elseReturnOrNot = false; 932 | if (!lexer.PeekNextToken().Token.equals("(")) { 933 | error("Error: in class: " + className + ", \"(\" is expected, line: " 934 | + lexer.PeekNextToken().LineNumber); 935 | } else { 936 | lexer.GetNextToken(); 937 | } 938 | expression(); 939 | 940 | if (!lexer.PeekNextToken().Token.equals(")")) { 941 | error("Error: in class: " + className + ", \")\" is expected, line: " 942 | + lexer.PeekNextToken().LineNumber); 943 | } else { 944 | lexer.GetNextToken(); 945 | } 946 | int currentIfCounter = ifCounter; 947 | vmCodeInput("if-goto IF_TRUE" + currentIfCounter + "\n"); 948 | vmCodeInput("goto IF_FALSE" + currentIfCounter + "\n"); 949 | 950 | if (!lexer.PeekNextToken().Token.equals("{")) { 951 | error("Error: in class: " + className + ", \"{\" is expected, line: " 952 | + lexer.PeekNextToken().LineNumber); 953 | } else { 954 | vmCodeInput("label IF_TRUE" + currentIfCounter + "\n"); 955 | lexer.GetNextToken(); 956 | ifCounter++; 957 | } 958 | //Check the grammar of the source codes in the braces behind the if keyword 959 | Token newToken = lexer.PeekNextToken(); 960 | try { 961 | while (lexer.getReadIndex() < textContent.length() - 2) { 962 | 963 | if (newToken.Token.equals("let")) { 964 | lexer.GetNextToken(); 965 | letStatementCheck(); 966 | } else if (newToken.Token.equals("if")) { 967 | lexer.GetNextToken(); 968 | ifStatementCheck(); 969 | } else if (newToken.Token.equals("while")) { 970 | lexer.GetNextToken(); 971 | whileStatementCheck(); 972 | } else if (newToken.Token.equals("do")) { 973 | lexer.GetNextToken(); 974 | doStatementCheck(); 975 | } else if (newToken.Token.equals("return")) { 976 | ifReturnOrNot = true; 977 | lexer.GetNextToken(); 978 | returnStatementCheck(); 979 | } 980 | 981 | newToken = lexer.PeekNextToken(); 982 | 983 | if (newToken.Token.equals("}")) { 984 | break; 985 | } 986 | } 987 | } catch (Exception e) { 988 | e.printStackTrace(); 989 | } 990 | 991 | if (!lexer.PeekNextToken().Token.equals("}")) { 992 | error("Error: in class: " + className + ", \"}\" is expected, line: " 993 | + lexer.PeekNextToken().LineNumber); 994 | } else { 995 | oldToken = lexer.PeekNextToken().Token; 996 | lexer.GetNextToken(); 997 | } 998 | //Check the grammar of the source codes in the braces behind the else keyword 999 | if (lexer.PeekNextToken().Token.equals("else")) { 1000 | vmCodeInput("goto IF_END" + currentIfCounter + "\n"); 1001 | vmCodeInput("label IF_FALSE" + currentIfCounter + "\n"); 1002 | lexer.GetNextToken(); 1003 | if (!lexer.PeekNextToken().Token.equals("{")) { 1004 | error("Error: in class: " + className + ", \"{\" is expected, line: " 1005 | + lexer.PeekNextToken().LineNumber); 1006 | } else { 1007 | lexer.GetNextToken(); 1008 | } 1009 | newToken = lexer.PeekNextToken(); 1010 | try { 1011 | while (lexer.getReadIndex() < textContent.length() - 2) { 1012 | 1013 | if (newToken.Token.equals("let")) { 1014 | lexer.GetNextToken(); 1015 | letStatementCheck(); 1016 | } else if (newToken.Token.equals("if")) { 1017 | lexer.GetNextToken(); 1018 | ifStatementCheck(); 1019 | 1020 | } else if (newToken.Token.equals("while")) { 1021 | lexer.GetNextToken(); 1022 | whileStatementCheck(); 1023 | } else if (newToken.Token.equals("do")) { 1024 | lexer.GetNextToken(); 1025 | doStatementCheck(); 1026 | } else if (newToken.Token.equals("return")) { 1027 | elseReturnOrNot = true; 1028 | lastFunctionName = functionName; 1029 | lexer.GetNextToken(); 1030 | returnStatementCheck(); 1031 | } 1032 | 1033 | newToken = lexer.PeekNextToken(); 1034 | 1035 | if (newToken.Token.equals("}")) { 1036 | break; 1037 | } 1038 | } 1039 | } catch (Exception e) { 1040 | e.printStackTrace(); 1041 | } 1042 | if (!lexer.PeekNextToken().Token.equals("}")) { 1043 | error("Error: in class: " + className + ", \"}\" is expected, line: " 1044 | + lexer.PeekNextToken().LineNumber); 1045 | } else { 1046 | oldToken = lexer.PeekNextToken().Token; 1047 | lexer.GetNextToken(); 1048 | } 1049 | vmCodeInput("label IF_END" + currentIfCounter + "\n"); 1050 | } else { 1051 | vmCodeInput("label IF_FALSE" + currentIfCounter + "\n"); 1052 | } 1053 | 1054 | if (ifReturnOrNot && elseReturnOrNot) { 1055 | lastFunctionName = functionName; 1056 | } 1057 | } 1058 | //This methd is used to check the grammar of the while loop and insert corresponding VM codes 1059 | private void whileStatementCheck() throws Exception {//need to process the } 1060 | int currentIfCounter = whileCounter; 1061 | vmCodeInput("label WHILE_EXP" + currentIfCounter + "\n"); 1062 | if (!lexer.PeekNextToken().Token.equals("(")) { 1063 | error("Error: in class: " + className 1064 | + ", \"(\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1065 | } else { 1066 | lexer.GetNextToken(); 1067 | } 1068 | expression(); 1069 | 1070 | if (!lexer.PeekNextToken().Token.equals(")")) { 1071 | error("Error: in class: " + className 1072 | + ", \")\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1073 | } else { 1074 | lexer.GetNextToken(); 1075 | } 1076 | vmCodeInput("not\n"); 1077 | vmCodeInput("if-goto WHILE_END" + currentIfCounter + "\n"); 1078 | //Check the grammar of the source codes in the braces behind the while keyword 1079 | if (!lexer.PeekNextToken().Token.equals("{")) { 1080 | error("Error: in class: " + className 1081 | + ", \"{\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1082 | } else { 1083 | whileCounter++; 1084 | lexer.GetNextToken(); 1085 | } 1086 | Token newToken = lexer.PeekNextToken(); 1087 | try { 1088 | while (lexer.getReadIndex() < textContent.length() - 2) { 1089 | 1090 | if (newToken.Token.equals("}")) { 1091 | break; 1092 | } 1093 | if (newToken.Token.equals("var")) { 1094 | lexer.GetNextToken(); 1095 | functionVarDeclarCheck(); 1096 | } else if (newToken.Token.equals("let")) { 1097 | lexer.GetNextToken(); 1098 | letStatementCheck(); 1099 | } else if (newToken.Token.equals("if")) { 1100 | lexer.GetNextToken(); 1101 | ifStatementCheck(); 1102 | } else if (newToken.Token.equals("while")) { 1103 | lexer.GetNextToken(); 1104 | whileStatementCheck(); 1105 | } else if (newToken.Token.equals("do")) { 1106 | lexer.GetNextToken(); 1107 | doStatementCheck(); 1108 | } else if (newToken.Token.equals("return")) { 1109 | lexer.GetNextToken(); 1110 | returnStatementCheck(); 1111 | } 1112 | newToken = lexer.PeekNextToken(); 1113 | } 1114 | } catch (Exception e) { 1115 | 1116 | } 1117 | if (!lexer.PeekNextToken().Token.equals("}")) { 1118 | error("Error: in class: " + className 1119 | + ", \"}\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1120 | } else { 1121 | oldToken = lexer.PeekNextToken().Token; 1122 | lexer.GetNextToken(); 1123 | } 1124 | vmCodeInput("goto WHILE_EXP" + currentIfCounter + "\n"); 1125 | vmCodeInput("label WHILE_END" + currentIfCounter + "\n"); 1126 | } 1127 | //This method is used to check the grammar of the do statement 1128 | private void doStatementCheck() { 1129 | try { 1130 | subroutineCallCheck(); 1131 | if (!lexer.PeekNextToken().Token.equals(";")) { 1132 | error("Error: in class: " + className 1133 | + ", \";\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1134 | } else { 1135 | oldToken = lexer.PeekNextToken().Token; 1136 | lexer.GetNextToken(); 1137 | } 1138 | } catch (Exception e) { 1139 | e.printStackTrace(); 1140 | } 1141 | } 1142 | //This method is used to check the grammar of the subroutine call 1143 | private void subroutineCallCheck() throws Exception { 1144 | Token lastToken = null; 1145 | String subroutineName = ""; 1146 | 1147 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 1148 | error("Error: in class: " + className 1149 | + ", identifier is expected, line: " + lexer.PeekNextToken().LineNumber); 1150 | } else { 1151 | lastToken = lexer.PeekNextToken(); 1152 | subroutineName = lastToken.Token; 1153 | lexer.GetNextToken(); 1154 | } 1155 | 1156 | //Used to check the type like identifier.identifier 1157 | if (lexer.PeekNextToken().Token.equals(".")) { 1158 | String arguments = ""; 1159 | String methodCallLexeme = ""; 1160 | boolean methodCallOrNot = false; 1161 | lexer.GetNextToken(); 1162 | subroutineName += "." + lexer.PeekNextToken().Token; 1163 | //Use the if statements to exclude each possibility to find the grammar mistakes 1164 | if (lexer.PeekNextToken().Type.toString().equals("ID")) { 1165 | if (!jackClasses.functionsArguments.containsKey(subroutineName)) { 1166 | if (!(symbolTables[4].findFunctionSymbol(subroutineName) 1167 | && (symbolTables[4].functionType.equals("function") 1168 | || symbolTables[4].functionType.equals("constructor")))) { 1169 | String objectFunction = subroutineName.split("\\.")[1]; 1170 | if (symbolTables[2].findIdentifierSymbol(lastToken.Token) 1171 | && (symbolTables[4].findFunctionSymbol(symbolTables[2].dataType + "." + objectFunction) 1172 | || jackClasses.methodMatch(symbolTables[2].dataType + "." + objectFunction))) { 1173 | methodCallOrNot = true; 1174 | methodCallLexeme = symbolTables[2].dataType + "." + objectFunction; 1175 | 1176 | vmCodeInput("push " + symbolTables[2].memorySegment + " " + symbolTables[2].offset + "\n"); 1177 | pushCounter++; 1178 | if (!(symbolTables[4].functionType.equals("method") 1179 | || jackClasses.methodMatch(symbolTables[2].dataType + "." + objectFunction))) { 1180 | error("Error: in class: " + className + ", function calling is not a method, line: " + lastToken.LineNumber); 1181 | } 1182 | } else if (symbolTables[3].findIdentifierSymbol(lastToken.Token) 1183 | && (symbolTables[4].findFunctionSymbol(symbolTables[3].dataType + "." + objectFunction) 1184 | || jackClasses.methodMatch(symbolTables[3].dataType + "." + objectFunction))) { 1185 | methodCallOrNot = true; 1186 | methodCallLexeme = symbolTables[3].dataType + "." + objectFunction; 1187 | vmCodeInput("push " + symbolTables[3].memorySegment + " " + symbolTables[3].offset + "\n"); 1188 | pushCounter++; 1189 | if (!(symbolTables[4].functionType.equals("method") 1190 | || jackClasses.methodMatch(symbolTables[3].dataType + "." + objectFunction))) { 1191 | error("Error: in class: " + className + ", function calling is not a method, line: " + lastToken.LineNumber); 1192 | } 1193 | } else if (symbolTables[1].findIdentifierSymbol(lastToken.Token) 1194 | && (symbolTables[4].findFunctionSymbol(symbolTables[1].dataType + "." + objectFunction) 1195 | || jackClasses.methodMatch(symbolTables[1].dataType + "." + objectFunction))) { 1196 | methodCallOrNot = true; 1197 | methodCallLexeme = symbolTables[1].dataType + "." + objectFunction; 1198 | vmCodeInput("push " + symbolTables[1].memorySegment + " " + symbolTables[1].offset + "\n"); 1199 | pushCounter++; 1200 | if (!(symbolTables[4].functionType.equals("method") 1201 | || jackClasses.methodMatch(symbolTables[1].dataType + "." + objectFunction))) { 1202 | error("Error: in class: " + className + ", function calling is not a method, line: " + lastToken.LineNumber); 1203 | } 1204 | } else { 1205 | error("Error: in class: " + className + ", function called \"" 1206 | + subroutineName + "\" is not defined, line: " + lastToken.LineNumber); 1207 | } 1208 | //Release the array after the dispose method has been called 1209 | if (methodCallLexeme.equals("Array.dispose")) { 1210 | for (int tra = 3; tra >= 0; tra--) { 1211 | if (symbolTables[tra].findIdentifierSymbol(lastToken.Token)) { 1212 | symbolTables[tra].setInitToNot(lastToken.Token); 1213 | break; 1214 | } 1215 | } 1216 | } 1217 | 1218 | } 1219 | } 1220 | //Check the grammar of argument list 1221 | lexer.GetNextToken(); 1222 | if (lexer.PeekNextToken().Token.equals("(")) { 1223 | if (methodCallOrNot) { 1224 | pushCounter = 1; 1225 | } else { 1226 | pushCounter = 0; 1227 | } 1228 | lexer.GetNextToken(); 1229 | while (!lexer.PeekNextToken().Token.equals(")")) { 1230 | int currentPushCounter = pushCounter; 1231 | expressionReturnType = ""; 1232 | expression(); 1233 | pushCounter = currentPushCounter + 1; 1234 | if (Pattern.compile(otherClassType).matcher(expressionReturnType).matches()) { 1235 | arguments += "class "; 1236 | } else { 1237 | arguments += expressionReturnType + " "; 1238 | } 1239 | if (!lexer.PeekNextToken().Token.equals(",")) { 1240 | break; 1241 | } else { 1242 | lexer.GetNextToken(); 1243 | } 1244 | } 1245 | if (!lexer.PeekNextToken().Token.equals(")")) { 1246 | error("Error: in class: " + className + ", \")\" is expected, line: " 1247 | + lexer.PeekNextToken().LineNumber); 1248 | } else { 1249 | lexer.GetNextToken(); 1250 | } 1251 | } 1252 | if (!(Pattern.compile(symbolTables[4]. 1253 | getArgument(subroutineName)). 1254 | matcher(arguments).matches() 1255 | || Pattern.compile(symbolTables[4]. 1256 | getArgument(methodCallLexeme)). 1257 | matcher(arguments).matches()) 1258 | && !((jackClasses.functionsArguments.containsKey(subroutineName) 1259 | && Pattern.compile(((String) jackClasses. 1260 | functionsArguments.get(subroutineName))). 1261 | matcher(arguments).matches()) 1262 | || (jackClasses.functionsArguments.containsKey(methodCallLexeme) 1263 | && Pattern.compile(((String) jackClasses. 1264 | functionsArguments.get(methodCallLexeme))). 1265 | matcher(arguments).matches()))) { 1266 | error("Error: in class: " + className + ", function \"" 1267 | + subroutineName + "\" got wrong arguments, line:" + lastToken.LineNumber); 1268 | } 1269 | //Match the subroutine name with the different symbol tables to insert corresponding VM codes 1270 | if (jackClasses.returnTypes.containsKey(subroutineName)) { 1271 | expressionReturnType = (String) jackClasses.returnTypes.get(subroutineName); 1272 | vmCodeInput("call " + subroutineName + " " + pushCounter + "\n"); 1273 | pushCounter = 0; 1274 | } else if (jackClasses.returnTypes.containsKey(methodCallLexeme)) { 1275 | expressionReturnType = (String) jackClasses.returnTypes.get(methodCallLexeme); 1276 | vmCodeInput("call " + methodCallLexeme + " " + pushCounter + "\n"); 1277 | pushCounter = 0; 1278 | } else if (!methodCallOrNot && symbolTables[4].findFunctionSymbol(subroutineName)) { 1279 | expressionReturnType = symbolTables[4].returnType; 1280 | vmCodeInput("call " + subroutineName + " " + pushCounter + "\n"); 1281 | pushCounter = 0; 1282 | } else if (symbolTables[4].findFunctionSymbol(methodCallLexeme)) { 1283 | expressionReturnType = symbolTables[4].returnType; 1284 | vmCodeInput("call " + methodCallLexeme + " " + pushCounter + "\n"); 1285 | pushCounter = 0; 1286 | } 1287 | vmCodeInput("pop temp 0\n"); 1288 | } else { 1289 | error("Error: in class: " + className + ", identifier is expected, line: " + lexer.PeekNextToken().LineNumber); 1290 | } 1291 | //Check the grammar of the subroutine call like 'identifier()' 1292 | } else if (lexer.PeekNextToken().Token.equals("(")) { 1293 | String arguments = ""; 1294 | if (!symbolTables[4].findFunctionSymbol(className + "." + subroutineName)) { 1295 | error("Error: in class: " + className + ", function: \"" 1296 | + lexer.PeekNextToken().Token + "\" is not defined, line:" + lexer.PeekNextToken().LineNumber); 1297 | } else { 1298 | 1299 | if (symbolTables[4].functionType.equals("method")) { 1300 | if (symbolTables[4].findFunctionSymbol(className + "." + functionName) 1301 | && (symbolTables[4].functionType.equals("method") || symbolTables[4].functionType.equals("constructor"))) { 1302 | vmCodeInput("push pointer 0\n"); 1303 | pushCounter++; 1304 | } else { 1305 | error("Error: in class: " + className + ", function calling for method happens in a non-method, line: " 1306 | + lexer.PeekNextToken().LineNumber); 1307 | } 1308 | } else if (symbolTables[4].functionType.equals("function")) { 1309 | error("Error: in class: " + className + ", function called as a method in a function, line: " 1310 | + lexer.PeekNextToken().LineNumber); 1311 | } 1312 | } 1313 | //Check the grammar of the argument list 1314 | lexer.GetNextToken(); 1315 | while (!lexer.PeekNextToken().Token.equals(")")) { 1316 | String currentArgumentType = lexer.PeekNextToken().Type.toString(); 1317 | 1318 | expressionReturnType = ""; 1319 | 1320 | expression(); 1321 | pushCounter++; 1322 | if (!expressionReturnType.equals("")) { 1323 | if (expressionReturnType.equals("Array")) { 1324 | arguments += "int "; 1325 | } else { 1326 | arguments += expressionReturnType + " "; 1327 | } 1328 | } else { 1329 | arguments += currentArgumentType + " "; 1330 | } 1331 | 1332 | if (!lexer.PeekNextToken().Token.equals(",")) { 1333 | break; 1334 | } else { 1335 | lexer.GetNextToken(); 1336 | } 1337 | } 1338 | if (!lexer.PeekNextToken().Token.equals(")")) { 1339 | error("Error: in class: " + className + 1340 | ", \")\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1341 | } else { 1342 | lexer.GetNextToken(); 1343 | } 1344 | //Check if the arguments in the subroutine call matches with the definition of the subroutine 1345 | if (!Pattern.compile(symbolTables[4].getArgument(className + "." + subroutineName)).matcher(arguments).matches()) { 1346 | error("Error: in class: " + className + 1347 | ", function \"" + subroutineName + "\" got wrong arguments, line:" + lastToken.LineNumber); 1348 | } 1349 | expressionReturnType = symbolTables[4].getFunctionReturnType(subroutineName); 1350 | 1351 | vmCodeInput("call " + className + "." + subroutineName + " " + pushCounter + "\n"); 1352 | vmCodeInput("pop temp 0\n"); 1353 | pushCounter = 0; 1354 | } 1355 | symbolTableVarClear(); 1356 | } 1357 | 1358 | /*This method is used to check the grammar of the return statement 1359 | and insert the VM codes for the return statement 1360 | */ 1361 | private void returnStatementCheck() throws Exception { 1362 | String valueReturned = ""; 1363 | if (!lexer.PeekNextToken().Token.equals(";")) { 1364 | valueReturned = lexer.PeekNextToken().Token; 1365 | expression(); 1366 | } else { 1367 | expressionReturnType = "void"; 1368 | vmCodeInput("push constant 0\n"); 1369 | } 1370 | //Check if the current subroutine is a constructor 1371 | if (symbolTables[4].findFunctionSymbol(className + "." + functionName) 1372 | && symbolTables[4].functionType.equals("constructor") 1373 | && !valueReturned.equals("this")) { 1374 | error("Error: in class: " + className + 1375 | ", the \"this\" is not referenced in a constructor, line: " + 1376 | lexer.PeekNextToken().LineNumber); 1377 | } 1378 | //Check if there is a subroutine returning this as the return value 1379 | if (valueReturned.equals("this") 1380 | && symbolTables[4].findFunctionSymbol(className + "." + functionName) 1381 | && symbolTables[4].functionType.equals("function")) { 1382 | error("Error: in class: " + className + 1383 | ", the \"this\" cannot be referenced in a function, line: " + 1384 | lexer.PeekNextToken().LineNumber); 1385 | } 1386 | 1387 | //Check if the data type of the return value matches with the return of the subroutine in the definition of subroutine 1388 | if (!expressionReturnType.equals(returnType)) { 1389 | error("Error: data type of return value not matches with the definition of function, line: " + lexer.PeekNextToken().LineNumber); 1390 | } 1391 | 1392 | if (!lexer.PeekNextToken().Token.equals(";")) { 1393 | error("Error: in class: " + className + ", \";\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1394 | } else { 1395 | lexer.GetNextToken(); 1396 | } 1397 | if (oldToken.equals(";") || oldToken.equals("}") || oldToken.equals("{")) { 1398 | if (!lexer.PeekNextToken().Token.equals("}")) { 1399 | error("Error: in class: " + className + ", unreachable codes, line: " + lexer.GetNextToken().LineNumber); 1400 | } 1401 | } 1402 | vmCodeInput("return\n"); 1403 | } 1404 | 1405 | //Divide expression into the relational expression 1406 | private void expression() throws Exception { 1407 | relationalExpression(); 1408 | String currentReturnType = expressionReturnType; 1409 | while (lexer.PeekNextToken().Token.equals("&") 1410 | || lexer.PeekNextToken().Token.equals("|")) { 1411 | String relationSymbol = lexer.PeekNextToken().Token; 1412 | 1413 | lexer.GetNextToken(); 1414 | relationalExpression(); 1415 | //Check if the data types of two sides match with each other 1416 | if (!((currentReturnType.equals("boolean") && expressionReturnType.equals("boolean")) 1417 | || (currentReturnType.equals("int") && expressionReturnType.equals("int")))) { 1418 | error("Error: in class: " + className + 1419 | ", two ends of relational expression are not all int, line: " + 1420 | lexer.PeekNextToken().LineNumber); 1421 | } 1422 | if (relationSymbol.equals("&")) { 1423 | vmCodeInput("and\n"); 1424 | } else { 1425 | vmCodeInput("or\n"); 1426 | } 1427 | expressionReturnType = "boolean"; 1428 | } 1429 | 1430 | } 1431 | //Divide relational expression into the arithmetic expression 1432 | private void relationalExpression() throws Exception { 1433 | ArithmeticExpression(); 1434 | String currentReturnType = ""; 1435 | if (jackClasses.dataTypeTransfer.containsKey(expressionReturnType)) { 1436 | currentReturnType = (String) jackClasses.dataTypeTransfer.get(expressionReturnType); 1437 | } else { 1438 | currentReturnType = (expressionReturnType + "|null|Array"); 1439 | } 1440 | 1441 | while (lexer.PeekNextToken().Token.equals("=") 1442 | || lexer.PeekNextToken().Token.equals("<") 1443 | || lexer.PeekNextToken().Token.equals(">")) { 1444 | String relationSymbol = lexer.PeekNextToken().Token; 1445 | lexer.GetNextToken(); 1446 | ArithmeticExpression(); 1447 | //Check if the data types of two sides match with each other 1448 | if (relationSymbol.equals("=")) { 1449 | if (!Pattern.compile(currentReturnType).matcher(expressionReturnType).matches()) { 1450 | error("Error: in class: " + className + 1451 | ", two ends of relational expression not have same type, line: " + 1452 | lexer.PeekNextToken().LineNumber); 1453 | } 1454 | vmCodeInput("eq\n"); 1455 | } else if (relationSymbol.equals("<")) { 1456 | if (!Pattern.compile(currentReturnType).matcher(expressionReturnType).matches()) { 1457 | error("Error: in class: " + className + 1458 | ", two ends of relational expression are not all int, line: " + 1459 | lexer.PeekNextToken().LineNumber); 1460 | } 1461 | vmCodeInput("lt\n"); 1462 | } else { 1463 | if (!Pattern.compile(currentReturnType).matcher(expressionReturnType).matches()) { 1464 | error("Error: in class: " + className + 1465 | ", two ends of relational expression are not all int, line: " + 1466 | lexer.PeekNextToken().LineNumber); 1467 | } 1468 | vmCodeInput("gt\n"); 1469 | } 1470 | expressionReturnType = "boolean"; 1471 | } 1472 | 1473 | } 1474 | //Divide arithmetic expression into the term 1475 | private void ArithmeticExpression() throws Exception { 1476 | term(); 1477 | 1478 | if (lexer.PeekNextToken().Token.equals("+") 1479 | || lexer.PeekNextToken().Token.equals("-")) { 1480 | 1481 | if (!(expressionReturnType.equals("int") 1482 | || expressionReturnType.equals("all"))) { 1483 | error("Error: in class: " + className + 1484 | ", \"int\" type is expected, line " + 1485 | lexer.PeekNextToken().LineNumber); 1486 | } 1487 | } 1488 | 1489 | while (lexer.PeekNextToken().Token.equals("+") 1490 | || lexer.PeekNextToken().Token.equals("-")) { 1491 | String arithmeticSymbol = ""; 1492 | if (lexer.PeekNextToken().Token.equals("+")) { 1493 | arithmeticSymbol = "add"; 1494 | } else { 1495 | arithmeticSymbol = "sub"; 1496 | } 1497 | lexer.GetNextToken(); 1498 | term(); 1499 | //Check if the data types of two sides match with each other 1500 | if (!(expressionReturnType.equals("int") 1501 | || expressionReturnType.equals("all"))) { 1502 | error("Error: in class: " + className + 1503 | ", \"int\" type is expected, line " + 1504 | lexer.PeekNextToken().LineNumber); 1505 | } 1506 | expressionReturnType = "int"; 1507 | vmCodeInput(arithmeticSymbol + "\n"); 1508 | } 1509 | } 1510 | //Divide term into the factor 1511 | private void term() throws Exception { 1512 | factor(); 1513 | if (lexer.PeekNextToken().Token.equals("*") 1514 | || lexer.PeekNextToken().Token.equals("/")) { 1515 | if (!(expressionReturnType.equals("int") 1516 | || expressionReturnType.equals("all"))) { 1517 | error("Error: in class: " + className + 1518 | ", \"int\" type is expected, line " + 1519 | lexer.PeekNextToken().LineNumber); 1520 | } 1521 | } 1522 | 1523 | while (lexer.PeekNextToken().Token.equals("*") 1524 | || lexer.PeekNextToken().Token.equals("/")) { 1525 | 1526 | String arithmeticSymbol = lexer.PeekNextToken().Token; 1527 | 1528 | lexer.GetNextToken(); 1529 | factor(); 1530 | //Check if the data types of two sides match with each other 1531 | if (!(expressionReturnType.equals("int") 1532 | || expressionReturnType.equals("all"))) { 1533 | error("Error: in class: " + className + 1534 | ", \"int\" type is expected, line " + 1535 | lexer.PeekNextToken().LineNumber); 1536 | } 1537 | 1538 | if (arithmeticSymbol.equals("*")) { 1539 | vmCodeInput("call Math.multiply 2\n"); 1540 | } else { 1541 | vmCodeInput("call Math.divide 2\n"); 1542 | } 1543 | expressionReturnType = "int"; 1544 | } 1545 | } 1546 | //Divide factor expression into the operand 1547 | private void factor() throws Exception { 1548 | String negOrNot = ""; 1549 | if (lexer.PeekNextToken().Token.equals("-") 1550 | || lexer.PeekNextToken().Token.equals("~")) { 1551 | negOrNot = lexer.PeekNextToken().Token; 1552 | lexer.GetNextToken(); 1553 | } 1554 | operand(); 1555 | //Check if the data types of two sides match with each other 1556 | if (negOrNot.equals("-")) { 1557 | if (!expressionReturnType.equals("int")) { 1558 | error("Error: in class: " + className + 1559 | ", \"int\" type is expected, line " + 1560 | lexer.PeekNextToken().LineNumber); 1561 | } 1562 | vmCodeInput("neg\n"); 1563 | } else if (negOrNot.equals("~")) { 1564 | if (!expressionReturnType.equals("boolean")) { 1565 | error("Error: in class: " + className + 1566 | ", \"boolean\" type is expected, line " + 1567 | lexer.PeekNextToken().LineNumber); 1568 | } 1569 | vmCodeInput("not\n"); 1570 | } 1571 | } 1572 | //This method is used to process all kinds of operands 1573 | private void operand() throws Exception { 1574 | //Identify the type of the operand and insert the VM code into the VM file 1575 | //If the operand is an integer 1576 | if (lexer.PeekNextToken().Type.toString().equals("Constant")) { 1577 | eleNumberCounter = Integer.parseInt(lexer.PeekNextToken().Token); 1578 | expressionReturnType = "int"; 1579 | vmCodeInput("push constant " + lexer.PeekNextToken().Token + "\n"); 1580 | lexer.GetNextToken(); 1581 | //If the first of the operand is an identifier 1582 | } else if (lexer.PeekNextToken().Type.toString().equals("ID")) { 1583 | Token lastToken = lexer.PeekNextToken(); 1584 | String lastLexeme = lastToken.Token; 1585 | lexer.GetNextToken(); 1586 | //If this operand has the format like 'identifier[[]|()]' 1587 | if (!lexer.PeekNextToken().Token.equals(".")) { 1588 | //Check if the operand is a variable by traversing all four symbol tables 1589 | if (symbolTables[2].findIdentifierSymbol(lastToken.Token) 1590 | || symbolTables[3].findIdentifierSymbol(lastToken.Token) 1591 | || symbolTables[1].findIdentifierSymbol(lastToken.Token) 1592 | || symbolTables[0].findIdentifierSymbol(className + "." + lastToken.Token)) { 1593 | String memorySegment = ""; 1594 | int offset = 0; 1595 | //Check if this variable is an array 1596 | if (lexer.PeekNextToken().Token.equals("[")) { 1597 | boolean variableOrNot = false; 1598 | for (int index = 3; index >= 0; index--) { 1599 | if (symbolTables[index].dataType.equals("Array")) { 1600 | memorySegment = symbolTables[index].memorySegment; 1601 | offset = symbolTables[index].offset; 1602 | variableOrNot = true; 1603 | break; 1604 | } 1605 | symbolTables[index].memorySegment = ""; 1606 | symbolTables[index].dataType = ""; 1607 | symbolTables[index].offset = 0; 1608 | } 1609 | if (!variableOrNot) { 1610 | error("Error: in class: " + className + 1611 | ", variable \"" + lastLexeme + "\" not exists, line:" + lastToken.LineNumber); 1612 | } 1613 | lexer.GetNextToken(); 1614 | expression(); 1615 | vmCodeInput("push " + memorySegment + " " + offset + "\n"); 1616 | if (!(expressionReturnType.equals("int") 1617 | || expressionReturnType.equals("all"))) { 1618 | error("Error: in class: " + className + 1619 | ", \"int value\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1620 | } 1621 | if (!lexer.PeekNextToken().Token.equals("]")) { 1622 | error("Error: in class: " + className + 1623 | ", \"]\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1624 | } else { 1625 | lexer.GetNextToken(); 1626 | 1627 | expressionReturnType = "all"; 1628 | } 1629 | 1630 | vmCodeInput("add\n"); 1631 | vmCodeInput("pop pointer 1\n"); 1632 | vmCodeInput("push that 0\n"); 1633 | //Check if this operand is a variable 1634 | } else { 1635 | if (symbolTables[2].findIdentifierSymbol(lastLexeme)) { 1636 | if (!symbolTables[2].initOrNot) { 1637 | error("Error: in class: " + className + 1638 | ", variable \"" + lastLexeme + "\" is not initialized, line: " + lastToken.LineNumber); 1639 | } 1640 | expressionReturnType = symbolTables[2].getIdentifierAssignDataType(lastLexeme); 1641 | vmCodeInput("push " + symbolTables[2].memorySegment + " " + symbolTables[2].offset + "\n"); 1642 | } else if (symbolTables[3].findIdentifierSymbol(lastLexeme)) { 1643 | expressionReturnType = symbolTables[3].getIdentifierAssignDataType(lastLexeme); 1644 | vmCodeInput("push " + symbolTables[3].memorySegment + " " + symbolTables[3].offset + "\n"); 1645 | } else if (symbolTables[1].findIdentifierSymbol(lastLexeme)) { 1646 | if (!symbolTables[1].initOrNot) { 1647 | error("Error: in class: " + className + 1648 | ", variable \"" + lastLexeme + "\" is not initialized, line: " + lastToken.LineNumber); 1649 | } 1650 | expressionReturnType = symbolTables[1].getIdentifierAssignDataType(lastLexeme); 1651 | vmCodeInput("push " + symbolTables[1].memorySegment + " " + symbolTables[1].offset + "\n"); 1652 | } else if (symbolTables[0].findIdentifierSymbol(className + "." + lastLexeme)) { 1653 | if (!symbolTables[0].initOrNot) { 1654 | error("Error: in class: " + className + 1655 | ", variable \"" + lastLexeme + "\" is not initialized, line: " + lastToken.LineNumber); 1656 | } 1657 | expressionReturnType = symbolTables[0].getIdentifierAssignDataType(className + "." + lastLexeme); 1658 | vmCodeInput("push " + symbolTables[0].memorySegment + " " + symbolTables[0].offset + "\n"); 1659 | } 1660 | } 1661 | //Check if the operand is a subroutine 1662 | } else if (symbolTables[4].findFunctionSymbol(className + "." + lastToken.Token)) { 1663 | //Check the grammar of this subroutine call 1664 | if (symbolTables[4].functionType.equals("method")) { 1665 | if (symbolTables[4].findFunctionSymbol(className + "." + functionName) 1666 | && (symbolTables[4].functionType.equals("method") || symbolTables[4].functionType.equals("constructor"))) { 1667 | vmCodeInput("push pointer 0\n"); 1668 | pushCounter++; 1669 | } else { 1670 | error("Error: in class: " + className + 1671 | ", function calling for method happens in a non-method, line: " + lexer.PeekNextToken().LineNumber); 1672 | } 1673 | } else if (symbolTables[4].functionType.equals("function")) { 1674 | error("Error: in class: " + className + 1675 | ", function called as a method in a function, line: " + lexer.PeekNextToken().LineNumber); 1676 | } 1677 | if (lexer.PeekNextToken().Token.equals("(")) { 1678 | String arguments = ""; 1679 | lexer.GetNextToken(); 1680 | 1681 | while (!lexer.PeekNextToken().Token.equals(")")) { 1682 | String currentArgumentType = lexer.PeekNextToken().Type.toString(); 1683 | expressionReturnType = ""; 1684 | expression(); 1685 | if (!expressionReturnType.equals("")) { 1686 | arguments += expressionReturnType + " "; 1687 | } else { 1688 | arguments += currentArgumentType + " "; 1689 | } 1690 | 1691 | if (!lexer.PeekNextToken().Token.equals(",")) { 1692 | break; 1693 | } else { 1694 | lexer.GetNextToken(); 1695 | } 1696 | } 1697 | if (!lexer.PeekNextToken().Token.equals(")")) { 1698 | error("Error: in class: " + className + 1699 | ", \")\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1700 | } else { 1701 | lexer.GetNextToken(); 1702 | } 1703 | 1704 | if (!Pattern.compile(symbolTables[4].getArgument(className + "." + lastToken.Token)).matcher(arguments).matches()) { 1705 | error("Error: in class: " + className + 1706 | ", arguments' type not match, line: " + lexer.PeekNextToken().LineNumber); 1707 | } 1708 | 1709 | expressionReturnType = symbolTables[4].getFunctionReturnType(className + "." + lastLexeme); 1710 | vmCodeInput("call " + className + 1711 | "." + lastLexeme + " " + symbolTables[4].numOfArgs + "\n"); 1712 | } 1713 | } else { 1714 | error("Error: in class: " + className + 1715 | ", variable or function: \"" + lastToken.Token + "\" is not defined, line: " + lastToken.LineNumber); 1716 | } 1717 | //Check if the operand has the format like 'identifier.identifier' 1718 | } else { 1719 | lexer.GetNextToken(); 1720 | if (!lexer.PeekNextToken().Type.toString().equals("ID")) { 1721 | error("Error: in class: " + className + 1722 | ", identifier is expected, line: " + lexer.PeekNextToken().LineNumber); 1723 | } else { 1724 | lastLexeme += ("." + lexer.PeekNextToken().Token); 1725 | boolean staticVarOrNot = false; 1726 | boolean methodCallOrNot = false; 1727 | String methodCallLexeme = ""; 1728 | String memorySegment = ""; 1729 | int offset = 0; 1730 | //Check if the subroutine belongs to the Jack libraries 1731 | if (jackClasses.functionsArguments.containsKey(lastLexeme)) { 1732 | if (lastLexeme.equals("Array.new")) { 1733 | arrayInitOrNot = true; 1734 | } 1735 | lexer.GetNextToken(); 1736 | //Check if the subroutine belongs to the subroutines defined before 1737 | } else if ((symbolTables[4].findFunctionSymbol(lastLexeme) 1738 | && (symbolTables[4].functionType.equals("function") 1739 | || symbolTables[4].functionType.equals("constructor"))) 1740 | || symbolTables[0].findIdentifierSymbol(lastLexeme)) { 1741 | if (symbolTables[4].findFunctionSymbol(lastLexeme)) { 1742 | } else { 1743 | staticVarOrNot = true; 1744 | if (lexer.PeekNextToken().Token.equals("[")) { 1745 | boolean variableOrNot = false; 1746 | if (symbolTables[0].findIdentifierSymbol(lastLexeme) 1747 | && symbolTables[0].dataType.equals("Array")) { 1748 | variableOrNot = true; 1749 | 1750 | } 1751 | if (!variableOrNot) { 1752 | error("Error: in class: " + className + ", variable \"" + lastLexeme + "\" not exists, line: " + lastToken.LineNumber); 1753 | } 1754 | lexer.GetNextToken(); 1755 | expression(); 1756 | 1757 | vmCodeInput("push " + memorySegment + " " + offset + "\n"); 1758 | if (!(expressionReturnType.equals("int") 1759 | || expressionReturnType.equals("all"))) { 1760 | error("Error: in class: " + className + 1761 | ", \"int value\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1762 | } 1763 | if (!lexer.PeekNextToken().Token.equals("]")) { 1764 | error("Error: in class: " + className + 1765 | ", \"]\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1766 | } else { 1767 | lexer.GetNextToken(); 1768 | expressionReturnType = "all"; 1769 | } 1770 | 1771 | vmCodeInput("add\n"); 1772 | vmCodeInput("pop pointer 1\n"); 1773 | vmCodeInput("push that 0\n"); 1774 | } 1775 | } 1776 | lexer.GetNextToken(); 1777 | } else { 1778 | //Check if the subroutine call belongs to the calls for methods 1779 | methodCallOrNot = true; 1780 | String objectFunction = lastLexeme.split("\\.")[1]; 1781 | if (symbolTables[2].findIdentifierSymbol(lastToken.Token) 1782 | && (symbolTables[4].findFunctionSymbol(symbolTables[2].dataType + "." + objectFunction) 1783 | || jackClasses.methodMatch(symbolTables[2].dataType + "." + objectFunction))) { 1784 | methodCallLexeme = symbolTables[2].dataType + "." + objectFunction; 1785 | if (!(symbolTables[4].functionType.equals("method") 1786 | || jackClasses.methodMatch(symbolTables[2].dataType + "." + objectFunction))) { 1787 | error("Error: in class: " + className + 1788 | ", function calling is not a method, line: " + lastToken.LineNumber); 1789 | } else { 1790 | vmCodeInput("push " + symbolTables[2].memorySegment + " " + symbolTables[2].offset + "\n"); 1791 | pushCounter++; 1792 | } 1793 | } else if (symbolTables[3].findIdentifierSymbol(lastToken.Token) 1794 | && (symbolTables[4].findFunctionSymbol(symbolTables[3].dataType + "." + objectFunction) 1795 | || jackClasses.methodMatch(symbolTables[3].dataType + "." + objectFunction))) { 1796 | methodCallLexeme = symbolTables[3].dataType + "." + objectFunction; 1797 | if (!(symbolTables[4].functionType.equals("method") 1798 | || jackClasses.methodMatch(symbolTables[3].dataType + "." + objectFunction))) { 1799 | error("Error: in class: " + className + 1800 | ", function calling is not a method, line: " + lastToken.LineNumber); 1801 | } else { 1802 | vmCodeInput("push " + symbolTables[3].memorySegment + " " + symbolTables[3].offset + "\n"); 1803 | pushCounter++; 1804 | } 1805 | } else if (symbolTables[1].findIdentifierSymbol(lastToken.Token) 1806 | && (symbolTables[4].findFunctionSymbol(symbolTables[1].dataType + "." + objectFunction) 1807 | || jackClasses.methodMatch(symbolTables[1].dataType + "." + objectFunction))) { 1808 | methodCallLexeme = symbolTables[1].dataType + "." + objectFunction; 1809 | if (!(symbolTables[4].functionType.equals("method") 1810 | || jackClasses.methodMatch(symbolTables[1].dataType + "." + objectFunction))) { 1811 | error("Error: in class: " + className + 1812 | ", function calling is not a method, line: " + lastToken.LineNumber); 1813 | } else { 1814 | vmCodeInput("push " + symbolTables[1].memorySegment + " " + symbolTables[1].offset + "\n"); 1815 | pushCounter++; 1816 | } 1817 | } else { 1818 | error("Error: in class: " + className + ", function calling is not defined, line: " + lastToken.LineNumber); 1819 | } 1820 | lexer.GetNextToken(); 1821 | } 1822 | //Check the grammar of the argument list 1823 | if (!staticVarOrNot && lexer.PeekNextToken().Token.equals("(")) { 1824 | String arguments = ""; 1825 | if (methodCallOrNot) { 1826 | pushCounter = 1; 1827 | } else { 1828 | pushCounter = 0; 1829 | } 1830 | lexer.GetNextToken(); 1831 | 1832 | while (!lexer.PeekNextToken().Token.equals(")")) { 1833 | String currentArgumentType = lexer.PeekNextToken().Type.toString(); 1834 | expressionReturnType = ""; 1835 | int currentPushCounter = pushCounter; 1836 | expression(); 1837 | pushCounter = currentPushCounter + 1; 1838 | 1839 | if (!expressionReturnType.equals("")) { 1840 | arguments += expressionReturnType + " "; 1841 | } else { 1842 | arguments += currentArgumentType + " "; 1843 | } 1844 | 1845 | if (!lexer.PeekNextToken().Token.equals(",")) { 1846 | break; 1847 | } else { 1848 | lexer.GetNextToken(); 1849 | } 1850 | } 1851 | 1852 | if (!lexer.PeekNextToken().Token.equals(")")) { 1853 | error("Error: in class: " + className + ", \")\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1854 | } else { 1855 | lexer.GetNextToken(); 1856 | } 1857 | //Check if the arguments in the subroutine call matches with the definitions of the subroutines 1858 | if (!(Pattern.compile(symbolTables[4].getArgument(lastLexeme)).matcher(arguments).matches() 1859 | || Pattern.compile(symbolTables[4].getArgument(methodCallLexeme)).matcher(arguments).matches()) 1860 | && !((jackClasses.functionsArguments.get(lastLexeme) != null 1861 | && Pattern.compile(((String) jackClasses.functionsArguments.get(lastLexeme))).matcher(arguments).matches()) 1862 | || (jackClasses.functionsArguments.get(methodCallLexeme) != null 1863 | && Pattern.compile(((String) jackClasses.functionsArguments.get(methodCallLexeme))).matcher(arguments).matches()))) { 1864 | error("Error: in class: " + className + 1865 | ", function \"" + lexer.PeekNextToken().Token + "\" got wrong arguments, line:" + lexer.PeekNextToken().LineNumber); 1866 | } 1867 | 1868 | if (jackClasses.returnTypes.containsKey(lastLexeme)) { 1869 | expressionReturnType = (String) jackClasses.returnTypes.get(lastLexeme); 1870 | vmCodeInput("call " + lastLexeme + " " + pushCounter + "\n"); 1871 | pushCounter = 0; 1872 | } else if (!methodCallOrNot) { 1873 | expressionReturnType = symbolTables[4].getFunctionReturnType(lastLexeme); 1874 | vmCodeInput("call " + lastLexeme + " " + pushCounter + "\n"); 1875 | pushCounter = 0; 1876 | } else { 1877 | expressionReturnType = symbolTables[4].getFunctionReturnType(methodCallLexeme); 1878 | if (expressionReturnType.equals("")) { 1879 | expressionReturnType = (String) jackClasses.returnTypes.get(methodCallLexeme); 1880 | } 1881 | vmCodeInput("call " + methodCallLexeme + " " + pushCounter + "\n"); 1882 | pushCounter = 0; 1883 | } 1884 | } 1885 | } 1886 | } 1887 | } else if (lexer.PeekNextToken().Token.equals("(")) { 1888 | lexer.GetNextToken(); 1889 | expression(); 1890 | if (!lexer.PeekNextToken().Token.equals(")")) { 1891 | error("Error: in class: " + className + ", \")\" is expected, line: " + lexer.PeekNextToken().LineNumber); 1892 | } else { 1893 | lexer.GetNextToken(); 1894 | } 1895 | //Check if the operand is a string and insert VM codes for string 1896 | } else if (lexer.PeekNextToken().Type.toString().equals("String")) { 1897 | expressionReturnType = "String"; 1898 | String currentString = lexer.PeekNextToken().Token; 1899 | vmCodeInput("push constant " + (currentString.length() - 2) + "\n"); 1900 | vmCodeInput("call String.new 1\n"); 1901 | for (int index = 1; index < currentString.length() - 1; index++) { 1902 | vmCodeInput("push constant " + (int) currentString.charAt(index) + "\n"); 1903 | vmCodeInput("call String.appendChar 2\n"); 1904 | } 1905 | lexer.GetNextToken(); 1906 | //Check if the operand is a boolean variable and insert VM codes for boolean 1907 | } else if (lexer.PeekNextToken().Token.equals("true")) { 1908 | expressionReturnType = "boolean"; 1909 | lexer.GetNextToken(); 1910 | vmCodeInput("push constant 0\n"); 1911 | vmCodeInput("not\n"); 1912 | //Check if the operand is a boolean variable and insert VM codes for boolean 1913 | } else if (lexer.PeekNextToken().Token.equals("false")) { 1914 | expressionReturnType = "boolean"; 1915 | lexer.GetNextToken(); 1916 | vmCodeInput("push constant 0\n"); 1917 | //Check if the operand is null and insert VM codes for null 1918 | } else if (lexer.PeekNextToken().Token.equals("null")) { 1919 | expressionReturnType = "null"; 1920 | lexer.GetNextToken(); 1921 | vmCodeInput("push constant 0\n"); 1922 | //Check if the operand is this and insert VM codes for this 1923 | } else if (lexer.PeekNextToken().Token.equals("this")) { 1924 | expressionReturnType = className; 1925 | lexer.GetNextToken(); 1926 | vmCodeInput("push pointer 0\n"); 1927 | } 1928 | symbolTableVarClear(); 1929 | } 1930 | 1931 | } 1932 | --------------------------------------------------------------------------------