diff --git a/.gitignore b/.gitignore index 0bf2afcbb..f21da166f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ out npm-debug.log* yarn-debug.log* yarn-error.log* +**/.vscode/numbered-bookmars.json +**/.antlr +.idea/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 6b93aff12..184dfbb0d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -34,6 +34,13 @@ ], "outFiles": ["${workspaceFolder}/extension/build/test/**/*.js"], "preLaunchTask": "${defaultBuildTask}" + }, + { + "type": "chrome", + "request": "launch", + "name": "Test web ide", + "url": "http://localhost:3000/web-ide", + "webRoot": "${workspaceFolder}" } ] } diff --git a/cli/src/index.ts b/cli/src/index.ts index a541b2b27..82accdf1e 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -7,7 +7,7 @@ import { testRunner } from "./testrunner.js"; import { NodeFileSystemAdapter } from "@davidsouther/jiffies/lib/esm/fs_node.js"; import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; import * as fsCore from "fs"; -import { compile } from "@nand2tetris/simulator/jack/compiler.js"; +import { compile } from "@nand2tetris/simulator/jack/anltr.compiler.js"; yargs(hideBin(process.argv)) .usage("$0 ") diff --git a/components/src/stores/compiler.store.ts b/components/src/stores/compiler.store.ts index 12de210cb..d098866c6 100644 --- a/components/src/stores/compiler.store.ts +++ b/components/src/stores/compiler.store.ts @@ -1,5 +1,8 @@ import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; -import { compile } from "@nand2tetris/simulator/jack/compiler.js"; +import { + compile, + validate, +} from "@nand2tetris/simulator/jack/anltr.compiler.js"; import { CompilationError } from "@nand2tetris/simulator/languages/base.js"; import { Dispatch, MutableRefObject, useContext, useMemo, useRef } from "react"; import { useImmerReducer } from "../react.js"; @@ -30,7 +33,10 @@ export type CompilerStoreDispatch = Dispatch<{ function classTemplate(name: string) { return `class ${name} {\n\n}\n`; } - +interface FileEntry { + name: string; + content: string; +} export function makeCompilerStore( setStatus: Action, dispatch: MutableRefObject, @@ -46,13 +52,14 @@ export function makeCompilerStore( state.title = undefined; }, - setFile( - state: CompilerPageState, - { name, content }: { name: string; content: string }, - ) { + setFile(state: CompilerPageState, { name, content }: FileEntry) { state.files[name] = content; state.isCompiled = false; - this.compile(state); + }, + + setFileAndValidate(state: CompilerPageState, entry: FileEntry) { + this.setFile(state, entry); + this.validate(state); }, // the keys of 'files' have to be the full file path, not basename @@ -62,10 +69,12 @@ export function makeCompilerStore( this.compile(state); }, - compile(state: CompilerPageState) { - const compiledFiles = compile(state.files); + _processCompilationResults( + state: CompilerPageState, + files: Record, + ) { state.compiled = {}; - for (const [name, compiled] of Object.entries(compiledFiles)) { + for (const [name, compiled] of Object.entries(files)) { if (typeof compiled === "string") { state.compiled[name] = { valid: true, @@ -83,6 +92,16 @@ export function makeCompilerStore( ); }, + validate(state: CompilerPageState) { + state.isCompiled = false; + this._processCompilationResults(state, validate(state.files)); + }, + + compile(state: CompilerPageState) { + state.isCompiled = false; + this._processCompilationResults(state, compile(state.files)); + }, + writeCompiled(state: CompilerPageState) { if (Object.values(state.compiled).every((compiled) => compiled.valid)) { for (const [name, compiled] of Object.entries(state.compiled)) { @@ -136,6 +155,16 @@ export function makeCompilerStore( await fs.writeFile(`${name}.jack`, content); } }, + async writeNewFile(name: string, content?: string) { + content ??= classTemplate(name); + dispatch.current({ + action: "setFileAndValidate", + payload: { name, content }, + }); + if (fs) { + await fs.writeFile(`${name}.jack`, content); + } + }, async reset() { fs = undefined; @@ -143,8 +172,12 @@ export function makeCompilerStore( }, async compile() { + dispatch.current({ action: "compile" }); dispatch.current({ action: "writeCompiled" }); }, + async validate() { + dispatch.current({ action: "validate" }); + }, }; const initialState: CompilerPageState = { diff --git a/package-lock.json b/package-lock.json index 3132a6955..004e3b206 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "web" ], "devDependencies": { + "concurrently": "^9.0.1", "cross-env": "^7.0.3", "shx": "^0.3.4" }, @@ -5340,6 +5341,24 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/antlr4ng": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/antlr4ng/-/antlr4ng-3.0.7.tgz", + "integrity": "sha512-e9VzrS6fErCUTmkZX2eKEMCsmYY87gVhmHkzmOXtUDgvwGhivUvtiHqlOByDx4Nd1PIQ0lMlhflOWEz6cGXqKA==", + "license": "BSD-3-Clause", + "peerDependencies": { + "antlr4ng-cli": "^2.0.0" + } + }, + "node_modules/antlr4ng-cli": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/antlr4ng-cli/-/antlr4ng-cli-2.0.0.tgz", + "integrity": "sha512-oAt5OSSYhRQn1PgahtpAP4Vp3BApCoCqlzX7Q8ZUWWls4hX59ryYuu0t7Hwrnfk796OxP/vgIJaqxdltd/oEvQ==", + "license": "BSD-3-Clause", + "bin": { + "antlr4ng": "index.js" + } + }, "node_modules/any-promise": { "version": "1.3.0", "license": "MIT" @@ -6679,6 +6698,78 @@ "version": "0.0.1", "license": "MIT" }, + "node_modules/concurrently": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.0.1.tgz", + "integrity": "sha512-wYKvCd/f54sTXJMSfV6Ln/B8UrfLBKOYa+lzc6CHay3Qek+LorVSBdMVfyewFhRbH0Rbabsk4D+3PL/VjQ5gzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "license": "MIT" @@ -20858,6 +20949,16 @@ "punycode": "^2.1.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "dev": true, @@ -22532,12 +22633,14 @@ "@nand2tetris/projects": "^1.0.0", "@nand2tetris/runner": "^1.0.0", "@types/node": "^20.14.2", + "antlr4ng": "^3.0.7", "ohm-js": "^17.1.0", "rxjs": "^7.8.1" }, "devDependencies": { "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", + "antlr4ng-cli": "^2.0.0", "babel-jest": "^29.7.0", "jest": "^29.7.0", "jest-ts-webcompat-resolver": "^1.0.0", diff --git a/simulator/package.json b/simulator/package.json index 16a81357c..3982e5023 100644 --- a/simulator/package.json +++ b/simulator/package.json @@ -22,12 +22,14 @@ "@nand2tetris/projects": "^1.0.0", "@nand2tetris/runner": "^1.0.0", "@types/node": "^20.14.2", + "antlr4ng": "^3.0.7", "ohm-js": "^17.1.0", "rxjs": "^7.8.1" }, "devDependencies": { "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", + "antlr4ng-cli": "^2.0.0", "babel-jest": "^29.7.0", "jest": "^29.7.0", "jest-ts-webcompat-resolver": "^1.0.0", @@ -35,6 +37,9 @@ }, "scripts": { "build": "tsc", - "test": "jest --verbose" + "test": "jest --verbose", + "test-jack": "npm run test -- -t \"Jack\"", + "test-jack-w": "npm run test-w -- -t \"Jack\"", + "gen": "cd src/languages/grammars && antlr4ng -Dlanguage=TypeScript JackLexer.g4 JackParser.g4 -o ../../jack/generated" } } diff --git a/simulator/src/jack/README.md b/simulator/src/jack/README.md new file mode 100644 index 000000000..dbf3739cc --- /dev/null +++ b/simulator/src/jack/README.md @@ -0,0 +1,25 @@ +# ANTLR Jack compiler + +ANTLR gives us ability to generate lexer and parser in the target programming language(typescript in this case) using grammar files for [lexer](src/languages/grammars/JackLexer.g4) and [parser](src/languages/grammars/JackParser.g4). + +After parsing is done we get a tree data structure as an output. To do anything useful with this this tree we can use the next 2 design patterns: + +1. Listener +2. Visitor + +You can read more about this design patterns and the difference between them in this [blog post](https://tomassetti.me/listeners-and-visitors/). You can also check out [antlr mega tutorial from the same company](https://tomassetti.me/antlr-mega-tutorial/). + +For jack we use next listeners: + +- Error listener - listens to lexer and parser errors +- Global symbol table listener - creates global symbol table (classes and subroutines symbols) +- Validator listener - validates jack program +- VM Writer listener - generates VM code + +# Regenerate Jack Lexer and Parser files + +Next command gives us ability to regenerate parser and lexer after we've changed the grammar (.g4 files) for [lexer](src/languages/grammars/JackLexer.g4) or [parser](src/languages/grammars/JackParser.g4) + +``` +npm run gen +``` diff --git a/simulator/src/jack/anltr.compiler.ts b/simulator/src/jack/anltr.compiler.ts new file mode 100644 index 000000000..da20d295f --- /dev/null +++ b/simulator/src/jack/anltr.compiler.ts @@ -0,0 +1,167 @@ +import { GlobalSymbolTableListener } from "./listener/global.symbol.listener.js"; +import { CustomErrorListener } from "./listener/error.listener.js"; +import { ValidatorListener } from "./listener/validator.listener.js"; +import { JackCompilerError } from "./error.js"; +import { VMWriter } from "./listener/vm.writer.listener.js"; +import { JackParser, ProgramContext } from "./generated/JackParser.js"; +import { JackLexer } from "./generated/JackLexer.js"; +import { CharStream, CommonTokenStream, ParseTreeWalker } from "antlr4ng"; +import { CompilationError } from "../languages/base.js"; + +export function compile( + files: Record, +): Record { + return _doWithTryCatch(files, Command.Compile); +} + +export function validate( + files: Record, +): Record { + return _doWithTryCatch(files, Command.Validate); +} +function _doWithTryCatch(files: Record, cmd: Command) { + try { + return _do(files, cmd); + } catch (err) { + const result: Record = {}; + console.error(err); + const keys = Object.keys(files); + for (const name of keys) { + result[name] = { + message: + "Something went wrong while compiling files. Please create a bug report", + } as CompilationError; + } + return result; + } +} + +export const Command = { + Compile: 1, + Validate: 2, +} as const; +export type Command = (typeof Command)[keyof typeof Command]; + +function _do( + files: Record, + cmd: Command, +): Record { + if (files.type == "LexerOrParserError") { + throw new Error("Expected tree but got a lexer or parser error"); + } + const result: Record = {}; + for (const name of Object.keys(files)) { + result[name] = ""; + } + const trees: Record = {}; + const errors: Record = {}; + const compiler = new JackCompiler(); + for (const [name, content] of Object.entries(files)) { + const treeOrErrors = compiler.parserAndBind(content); + if (Array.isArray(treeOrErrors)) { + errors[name] = toCompilerError(treeOrErrors); + } + trees[name] = treeOrErrors as ProgramContext; + } + if (Object.keys(errors).length > 0) { + for (const [name, error] of Object.entries(errors)) { + result[name] = error; + } + return result; + } + + for (const [name, tree] of Object.entries(trees)) { + let compiledOrValidatedOrErrors; + switch (cmd) { + case Command.Compile: + compiledOrValidatedOrErrors = compiler.compile(tree, name); + + if (Array.isArray(compiledOrValidatedOrErrors)) { + result[name] = toCompilerError(compiledOrValidatedOrErrors); + } else { + result[name] = compiledOrValidatedOrErrors; + } + break; + case Command.Validate: + compiledOrValidatedOrErrors = compiler.validate(tree, name); + if (Array.isArray(compiledOrValidatedOrErrors)) { + result[name] = toCompilerError(compiledOrValidatedOrErrors); + } else { + result[name] = ""; + } + break; + default: + throw new Error("Invalid command"); + } + } + return result; +} +function toCompilerError(errors: JackCompilerError[]): CompilationError { + const err = errors[0]; + return { + message: `Line ${err.span.line}: ${err.msg}`, + span: err.span, + } as CompilationError; +} + +export class JackCompiler { + private globalSymbolTableListener = new GlobalSymbolTableListener(); + private errorListener = new CustomErrorListener(); + validate( + tree: ProgramContext, + filename?: string, + ): ProgramContext | JackCompilerError[] { + if ( + Object.keys(this.globalSymbolTableListener.globalSymbolTable).length == 0 + ) { + throw new Error( + "Please populate global symbol table using parserAndBind method", + ); + } + const validator = new ValidatorListener( + this.globalSymbolTableListener.globalSymbolTable, + filename, + ); + ParseTreeWalker.DEFAULT.walk(validator, tree); + + return validator.errors.length > 0 ? validator.errors : tree; + } + compile( + tree: ProgramContext, + filename?: string, + ): string | JackCompilerError[] { + const treeOrErrors = this.validate(tree, filename); + if (Array.isArray(treeOrErrors)) { + const errors = treeOrErrors as JackCompilerError[]; + console.log("Errors in validator " + JSON.stringify(errors)); + return errors; + } + const validateTree = treeOrErrors as ProgramContext; + const vmWriter = new VMWriter( + this.globalSymbolTableListener.globalSymbolTable, + ); + ParseTreeWalker.DEFAULT.walk(vmWriter, validateTree); + return vmWriter.result; + } + + parserAndBind(src: string): ProgramContext | JackCompilerError[] { + const lexer = new JackLexer(CharStream.fromString(src)); + lexer.removeErrorListeners(); + lexer.addErrorListener(this.errorListener); + const tokenStream = new CommonTokenStream(lexer); + const parser = new JackParser(tokenStream); + parser.removeErrorListeners(); + parser.addErrorListener(this.errorListener); + const tree = parser.program(); + if (this.errorListener.errors.length > 0) { + console.log("Errors when parsing or lexing"); + return this.errorListener.errors; + } + ParseTreeWalker.DEFAULT.walk(this.globalSymbolTableListener, tree); + if (this.globalSymbolTableListener.errors.length > 0) { + console.log("Errors when creating global symbol table"); + return this.globalSymbolTableListener.errors; + } + return tree; + } +} diff --git a/simulator/src/jack/antlr.compiler.test.ts b/simulator/src/jack/antlr.compiler.test.ts new file mode 100644 index 000000000..d9819ce43 --- /dev/null +++ b/simulator/src/jack/antlr.compiler.test.ts @@ -0,0 +1,991 @@ +import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; +import { NodeFileSystemAdapter } from "@davidsouther/jiffies/lib/esm/fs_node.js"; +import { getTestResourcePath, testResourceDirs } from "./test.helper"; +import path from "path"; +import { ProgramContext } from "./generated/JackParser"; +import { JackCompiler } from "./anltr.compiler"; +describe("Jack compiler", () => { + let fs: FileSystem; + beforeEach(() => { + fs = new FileSystem(new NodeFileSystemAdapter()); + }); + test("static field", () => { + testCompiler( + `class A{ + static int a; + function void init(){ + let a=1; + return; + } + }`, + ` + function A.init 0 + push constant 1 + pop static 0 + push constant 0 + return + `, + ); + }); + test("field", () => { + testCompiler( + ` + class A{ + field int a; + method void init(){ + let a=1; + return; + } + }`, + ` + function A.init 0 + push argument 0 + pop pointer 0 + push constant 1 + pop this 0 + push constant 0 + return + `, + ); + }); + + test("function with parameters", () => { + const input = ` + class A{ + function void a(int a){ + var int b; + let b = a + 1; + return; + } + }`; + const expected = ` + function A.a 1 + push argument 0 + push constant 1 + add + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("method with parameters", () => { + const input = ` + class A{ + method void a(int a){ + var int b; + let b = a + 1; + return; + } + }`; + const expected = ` + function A.a 1 + push argument 0 + pop pointer 0 + push argument 1 + push constant 1 + add + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + + //Assign + test("boolean literal assign", () => { + const input = ` + class A{ + function void a(){ + var boolean b; + let b = true; + return; + } + }`; + const expected = ` + function A.a 1 + //true(-1) + push constant 1 + neg + //let b = + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("boolean literal assign using logical operators", () => { + const input = ` + class A { + function void a(){ + var boolean b; + let b = (1>2) & (3<4) | (5>6) & (7=7); + return; + } + }`; + const expected = ` + function A.a 1 + //1>2 + push constant 1 + push constant 2 + gt + //3<4 + push constant 3 + push constant 4 + lt + and + //5>6 + push constant 5 + push constant 6 + gt + or + //7=7 + push constant 7 + push constant 7 + eq + and + //let b = + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("char literal assign", () => { + const input = ` + class A{ + function void a(){ + var char b; + let b = 1; + return; + } + }`; + const expected = ` + function A.a 1 + push constant 1 + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("null literal assign", () => { + const input = ` + class A { + function void a(){ + var A b; + let b = null; + return; + } + }`; + const expected = ` + function A.a 1 + push constant 0 + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("this literal assign", () => { + const input = ` + class A{ + method void a(){ + var A b; + let b = this; + return; + } + } + `; + const expected = ` + function A.a 1 + // first arg is this + push argument 0 + pop pointer 0 + // let b= this + push pointer 0 + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("int literal assign", () => { + const input = ` + class A{ + function void a(){ + var int b; + let b = 1; + return; + } + }`; + const expected = ` + function A.a 1 + push constant 1 + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("string literal assign", () => { + const input = ` + class A{ + function void a(){ + var String b; + let b = "hello"; + return; + } + } + `; + const expected = ` + function A.a 1 + // string length + push constant 5 + //create string array + call String.new 1 + //append chars one by one + push constant 104 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 111 + call String.appendChar 2 + //leb b= + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment for binary operation", () => { + const input = ` + class A{ + function void a(){ + var int b; + let b = 2*3; + return; + } + }`; + const expected = ` + function A.a 1 + push constant 2 + push constant 3 + call Math.multiply 2 + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment for varname", () => { + const input = ` + class A{ + function void a(){ + var boolean b,c; + let c=true; + let b=c; + return; + } + } + `; + const expected = ` + function A.a 2 + // create true value (-1) + push constant 1 + neg + // assign to c + pop local 1 + //let b=c + push local 1 + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment for subroutine call", () => { + const input = ` + class A{ + function int a(){ + return 1; + } + function void b(){ + var boolean b; + let b = A.a(); + return; + } + } + `; + const expected = ` + function A.a 0 + push constant 1 + return + function A.b 1 + call A.a 0 + //let b = + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("assign array value to a local var", () => { + const input = ` + class A{ + function void a(){ + var Array arr; + var int a; + let arr = Array.new(10); + let a = arr[5]; + return; + } + }`; + const expected = ` + function A.a 2 + //array new + push constant 10 + call Array.new 1 + //let arr= + pop local 0 + //arr[5] + push constant 5 + push local 0 + add + pop pointer 1 + push that 0 + //let a = + pop local 1 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("assignment for array access", () => { + const input = ` + class A{ + function void a(){ + var Array arr; + let arr = Array.new(10); + let arr[5] = 1; + return; + } + }`; + const expected = ` + function A.a 1 + // let arr = Array.new(10); + push constant 10 + call Array.new 1 + pop local 0 + //arr[5] + push constant 5 + push local 0 + add + //=1 + push constant 1 + pop temp 0 + //assign + pop pointer 1 + push temp 0 + pop that 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assign array value to array value", () => { + const input = ` + class A{ + function void a(){ + var Array arr; + let arr = Array.new(10); + let arr[0] = arr[5]; + return; + } + }`; + const expected = ` + function A.a 1 + // let arr = Array.new(10); + push constant 10 + call Array.new 1 + pop local 0 + //arr[0] + push constant 0 + push local 0 + add + //arr[5] + push constant 5 + push local 0 + add + + //save arr[5] in temp + pop pointer 1 + push that 0 + pop temp 0 + + //assign to arr[0] + pop pointer 1 + push temp 0 + pop that 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment to -1", () => { + const input = ` + class A{ + function void a(){ + var int b; + let b = -1; + return; + } + }`; + const expected = ` + function A.a 1 + push constant 1 + neg + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment to ~1", () => { + const input = ` + class A{ + function void a(){ + var int b; + let b = ~1; + return; + } + }`; + const expected = ` + function A.a 1 + // ~1; + push constant 1 + not + // let b = + pop local 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("assignment for grouped expr", () => { + const input = ` + class A{ + function void a(){ + var int b; + let b = 2*(3+5); + return; + } + }`; + const expected = ` + function A.a 1 + push constant 2 + push constant 3 + push constant 5 + add + call Math.multiply 2 + pop local 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("if statement", () => { + const input = ` + class A{ + function void a(){ + var boolean b; + let b = true; + if(b){ + let b = false; + } + return; + } + }`; + const expected = ` + function A.a 1 + //let b=true + push constant 1 + neg + pop local 0 + //if(b) + push local 0 + not + //go to end of if + if-goto A_0 + //let b=false + push constant 0 + pop local 0 + label A_0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("if else statement", () => { + const input = ` + class A{ + function void a(){ + var boolean b; + var int a; + let b = true; + if(b){ + let a = 1; + }else{ + let a = 2; + } + return; + } + }`; + const expected = ` + function A.a 2 + //let b=true + push constant 1 + neg + pop local 0 + //if(b) + push local 0 + not + //go to else + if-goto A_0 + push constant 1 + pop local 1 + // go to end of if else + goto A_1 + label A_0 + push constant 2 + pop local 1 + label A_1 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("while constant", () => { + const input = ` + class A{ + function void a(){ + var int i; + let i = 1; + while(i<10){ + let i=i+1; + } + return; + } + }`; + const expected = ` + function A.a 1 + // let i = 1; + push constant 1 + pop local 0 + //while + label A_0 + // i<10 + push local 0 + push constant 10 + lt + // if not go to end + not + if-goto A_1 + // let i=i+1; + push local 0 + push constant 1 + add + pop local 0 + // go to beginning + goto A_0 + label A_1 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("function call", () => { + const input = ` + class A{ + function void b(){ + return; + } + function void a(){ + do A.b(); + return; + } + }`; + const expected = ` + function A.b 0 + push constant 0 + return + function A.a 0 + call A.b 0 + pop temp 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("var method call", () => { + const input = ` + class A{ + constructor A new(){ + return this; + } + method void b(){ + return; + } + function void a(){ + var A a; + let a = A.new(); + do a.b(); + return; + } + }`; + const expected = ` + function A.new 0 + push constant 0 + call Memory.alloc 1 + pop pointer 0 + push pointer 0 + return + function A.b 0 + push argument 0 + pop pointer 0 + push constant 0 + return + function A.a 1 + call A.new 0 + pop local 0 + push local 0 + call A.b 1 + pop temp 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("var method call with params", () => { + const input = ` + class A{ + constructor A new(){ + return this; + } + method int sum(int a, int b){ + return a+b; + } + function void a(){ + var A a; + var int b; + let a = A.new(); + let b= a.sum(1,2); + return; + } + }`; + const expected = ` + function A.new 0 + push constant 0 + call Memory.alloc 1 + pop pointer 0 + push pointer 0 + return + function A.sum 0 + push argument 0 + pop pointer 0 + push argument 1 + push argument 2 + add + return + function A.a 2 + call A.new 0 + pop local 0 + push local 0 + push constant 1 + push constant 2 + call A.sum 3 + pop local 1 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test("empty class constructor", () => { + const input = ` + class A{ + constructor A new(){ + return this; + } + }`; + const expected = ` + function A.new 0 + //create this + push constant 0 + call Memory.alloc 1 + pop pointer 0 + //return this + push pointer 0 + return + `; + testCompiler(input, expected); + }); + test("class with fields and a constructor", () => { + const input = ` + class A{ + field int a,b,c; + field char d,e,f; + constructor A new(){ + return this; + } + }`; + const expected = ` + function A.new 0 + push constant 6 + call Memory.alloc 1 + pop pointer 0 + push pointer 0 + return + `; + testCompiler(input, expected); + }); + + test("return literal", () => { + const input = ` + class A{ + method int a(){ + return 1; + } + }`; + const expected = ` + function A.a 0 + // push this + push argument 0 + pop pointer 0 + //return 1 + push constant 1 + return + `; + testCompiler(input, expected); + }); + test("method return void", () => { + const input = ` + class A{ + method void a(){ + return; + } + }`; + const expected = ` + function A.a 0 + push argument 0 + pop pointer 0 + //return + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("return simple expression", () => { + const input = ` + class A{ + function int a(){ + return 1+2*3; + } + }`; + const expected = ` + function A.a 0 + push constant 1 + push constant 2 + //1+2 + add + push constant 3 + //3*3 + call Math.multiply 2 + return + `; + testCompiler(input, expected); + }); + + test("nested expressions", () => { + const input = ` + class A{ + function void a(){ + var Array a, b, c; + + let a = Array.new(10); + let b = Array.new(5); + + let b[a[4]] = a[3] + 3; + return; + } + }`; + const expected = ` + function A.a 3 + // let a = Array.new(10); + push constant 10 + call Array.new 1 + pop local 0 + // let b = Array.new(5); + push constant 5 + call Array.new 1 + pop local 1 + //a[4] + push constant 4 + push local 0 + add + //what? + pop pointer 1 + push that 0 + push local 1 + add + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + push constant 3 + add + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 0 + return + `; + testCompiler(input, expected); + }); + + test("mutate args", () => { + const input = ` + class A{ + function void fill(Array a, int size) { + while (size > 0) { + let size = size - 1; + let a[size] = Array.new(3); + } + return; + } + }`; + const expected = ` + function A.fill 0 + label A_0 + //size>0 + push argument 1 + push constant 0 + gt + not + if-goto A_1 + //let size = size - 1 + push argument 1 + push constant 1 + sub + pop argument 1 + //let a[size] = Array.new(3); + push argument 1 + push argument 0 + add + push constant 3 + call Array.new 1 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + goto A_0 + label A_1 + push constant 0 + return + `; + testCompiler(input, expected); + }); + test.each(testResourceDirs)("%s", async (folder: string) => { + await testFilesInFolder(fs, folder); + }); +}); +async function testFilesInFolder( + fs: FileSystem, + folderInTestResources: string, +) { + const testFolder = getTestResourcePath(folderInTestResources); + const files = [...(await fs.readdir(testFolder))] + .filter((file) => file.endsWith(".jack")) + .map((file) => path.join(testFolder, file)); + const trees: Record = {}; + const compiler = new JackCompiler(); + for (const f of files) { + const input = await fs.readFile(f); + const treeOrErrors = compiler.parserAndBind(input); + if (Array.isArray(treeOrErrors)) { + throw new Error( + `Unexpected compilation errors: ${treeOrErrors.join("\n")}`, + ); + } + const tree = treeOrErrors as ProgramContext; + trees[f] = tree; + } + for (const f of files) { + const expected = await fs.readFile(f.replace(".jack", ".vm")); + const res = compiler.compile(trees[f]); + if (Array.isArray(res)) { + throw new Error(`Unexpected compilation errors: ${res.join("\n")}`); + } else { + expect(trimAndDeleteComments(res)).toEqual( + trimAndDeleteComments(expected), + ); + } + } +} + +function trimMultiline(text: string): string { + return text + .split("\n") + .map((line) => line.trim()) + .filter((str) => str.replace(/[\s\t]/g, "").length != 0) + .join("\n"); +} +function deleteComments(text: string): string { + return text + .split("\n") + .filter((str) => str.indexOf("//") == -1) + .join("\n"); +} + +const compose = (fn1: (a: T) => T, ...fns: Array<(a: T) => T>) => + fns.reduce((prevFn, nextFn) => (value) => prevFn(nextFn(value)), fn1); + +const trimAndDeleteComments = compose(trimMultiline, deleteComments); +function testCompiler(input: string, expected: string): void { + const compiler = new JackCompiler(); + const treeOrErrors = compiler.parserAndBind(input); + if (Array.isArray(treeOrErrors)) { + throw new Error( + `Unexpected compilation errors: ${treeOrErrors.join("\n")}`, + ); + } + const tree = treeOrErrors as ProgramContext; + const res = compiler.compile(tree); + if (Array.isArray(res)) { + throw new Error(`Unexpected compilation errors: ${res.join("\n")}`); + } else { + expect(trimAndDeleteComments(res)).toEqual(trimAndDeleteComments(expected)); + } +} diff --git a/simulator/src/jack/builtins.ts b/simulator/src/jack/builtins.ts new file mode 100644 index 000000000..33c3ed445 --- /dev/null +++ b/simulator/src/jack/builtins.ts @@ -0,0 +1,93 @@ +import { GenericSymbol, SubroutineType } from "./symbol.js"; + +export const builtInTypes = ["int", "boolean", "char"]; +interface Range { + min: number; + max: number; +} + +export const intRange: Range = { min: -32768, max: 32767 }; + +const builtInFunctionsToArgCount: Record = { + "Array.dispose": 0, + "Array.init": 0, + "Array.new": 1, + "Keyboard.init": 0, + "Keyboard.keyPressed": 0, + "Keyboard.readChar": 0, + "Keyboard.readInt": 1, + "Keyboard.readLine": 1, + "Math.abs": 1, + "Math.divide": 2, + "Math.init": 0, + "Math.max": 2, + "Math.min": 2, + "Math.multiply": 2, + "Math.sqrt": 1, + "Memory.alloc": 1, + "Memory.deAlloc": 1, + "Memory.init": 0, + "Memory.peek": 1, + "Memory.poke": 2, + "Output.backSpace": 0, + "Output.init": 0, + "Output.moveCursor": 2, + "Output.printChar": 1, + "Output.printInt": 1, + "Output.println": 0, + "Output.printString": 1, + "Screen.clearScreen": 0, + "Screen.drawCircle": 3, + "Screen.drawLine": 4, + "Screen.drawPixel": 2, + "Screen.drawRectangle": 4, + "Screen.init": 0, + "Screen.setColor": 1, + "String.appendChar": 1, + "String.backSpace": 0, + "String.charAt": 2, + "String.dispose": 0, + "String.doubleQuote": 0, + "String.eraseLastChar": 0, + "String.init": 0, + "String.intValue": 0, + "String.length": 0, + "String.new": 1, + "String.newLine": 0, + "String.setCharAt": 1, + "String.setInt": 1, + "Sys.error": 1, + "Sys.halt": 0, + "Sys.wait": 1, +}; + +const builtInClasses = [ + "Array", + "Keyboard", + "Math", + "Memory", + "Output", + "Screen", + "String", + "Sys", +]; +const builtInClassesRecord = builtInClasses.reduce( + (acc, elem) => ({ + ...acc, + [elem]: {} as GenericSymbol, + }), + {} as Record, +); + +export const builtInSymbols = Object.keys(builtInFunctionsToArgCount).reduce( + (acc, elem) => ({ + ...acc, + [elem]: { + subroutineInfo: { + paramsCount: builtInFunctionsToArgCount[elem], + type: SubroutineType.Function, + }, + } as GenericSymbol, + }), + builtInClassesRecord, +); diff --git a/simulator/src/jack/error.ts b/simulator/src/jack/error.ts new file mode 100644 index 000000000..8d9e28e06 --- /dev/null +++ b/simulator/src/jack/error.ts @@ -0,0 +1,233 @@ +import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; +import { ParserRuleContext, TerminalNode } from "antlr4ng"; + +export interface Span { + start: number; + end: number; + line: number; +} +export const terminalNodeToSpan = (node: TerminalNode): Span => { + const s = node.getSymbol(); + return { + line: s.line, + start: s.start, + end: s.stop + 1, + }; +}; + +export const ruleContextToSpan = (ctx: ParserRuleContext): Span => { + const start = assertExists( + ctx.start, + " Cannot find start token when creating an error", + ); + return { + line: start.line, + start: start.start, + end: ctx.stop ? ctx.stop.stop + 1 : start.stop, + }; +}; + +export type JackCompilerErrorType = + | "ConstructorMushReturnThisError" + | "DuplicatedClassError" + | "DuplicatedSubroutineError" + | "DuplicatedVariableError" + | "FieldCantBeReferencedInFunctionError" + | "FilenameDoesntMatchClassNameError" + | "FunctionCalledAsMethodError" + | "IncorrectConstructorReturnTypeError" + | "IncorrectParamsNumberInSubroutineCallError" + | "IntLiteralIsOutOfRangeError" + | "LexerOrParserError" + | "MethodCalledAsFunctionError" + | "NonVoidFunctionNoReturnError" + | "SubroutineNotAllPathsReturnError" + | "ThisCantBeReferencedInFunctionError" + | "UndeclaredVariableError" + | "UnknownClassError" + | "UnknownSubroutineCallError" + | "UnreachableCodeError" + | "VoidSubroutineReturnsValueError" + | "WrongLiteralTypeError"; + +export interface JackCompilerError { + type: JackCompilerErrorType; + span: Span; + msg: string; +} + +export function makeJackCompilerError( + type: JackCompilerErrorType, + span: Span, + msg: string, +): JackCompilerError { + return { + type, + span, + msg, + }; +} + +export const ConstructorMushReturnThisError = (span: Span) => + makeJackCompilerError( + "ConstructorMushReturnThisError", + span, + `A constructor must return 'this'`, + ); + +export const DuplicatedClassError = (span: Span, className: string) => + makeJackCompilerError( + "DuplicatedClassError", + span, + `Class ${className} is already defined.`, + ); + +export const DuplicatedSubroutineError = (span: Span, subroutineName: string) => + makeJackCompilerError( + "DuplicatedSubroutineError", + span, + `Subroutine ${subroutineName} is already defined.`, + ); + +export const DuplicatedVariableError = (span: Span, variableName: string) => + makeJackCompilerError( + "DuplicatedVariableError", + span, + `Duplicated local variable, field, argument or static variable ${variableName}`, + ); + +export const FieldCantBeReferencedInFunctionError = (span: Span) => + makeJackCompilerError( + "FieldCantBeReferencedInFunctionError", + span, + `Field can't be referenced in a function`, + ); + +export const FilenameDoesntMatchClassNameError = ( + span: Span, + filename: string, + className: string, +) => + makeJackCompilerError( + "FilenameDoesntMatchClassNameError", + span, + `Class name ${className} doesn't match file name ${filename}`, + ); + +export const FunctionCalledAsMethodError = (span: Span, subroutineId: string) => + makeJackCompilerError( + "FunctionCalledAsMethodError", + span, + `Function or constructor ${subroutineId} was called as a method`, + ); + +export const IncorrectConstructorReturnTypeError = (span: Span) => + makeJackCompilerError( + "IncorrectConstructorReturnTypeError", + span, + `The return type of a constructor must be of the class type`, + ); + +export const IncorrectParamsNumberInSubroutineCallError = ( + span: Span, + subroutineName: string, + expectedParamsCount: number, + actualParamsCount: number, +) => + makeJackCompilerError( + "IncorrectParamsNumberInSubroutineCallError", + span, + `Expected ${expectedParamsCount} arguments, but got ${actualParamsCount}`, + ); + +export const IntLiteralIsOutOfRangeError = ( + span: Span, + value: number, + min: number, + max: number, +) => + makeJackCompilerError( + "IntLiteralIsOutOfRangeError", + span, + `Integer constant(${value}) is out of range. Min value is ${min} and max value is ${max}`, + ); + +export const LexerOrParserError = (span: Span, msg: string) => + makeJackCompilerError("LexerOrParserError", span, msg); + +export const MethodCalledAsFunctionError = (span: Span, subroutineId: string) => + makeJackCompilerError( + "MethodCalledAsFunctionError", + span, + `Method ${subroutineId} was called as a function/constructor`, + ); + +export const NonVoidFunctionNoReturnError = (span: Span) => + makeJackCompilerError( + "NonVoidFunctionNoReturnError", + span, + `A non void subroutine must return a value`, + ); + +export const SubroutineNotAllPathsReturnError = ( + span: Span, + subroutineName: string, +) => + makeJackCompilerError( + "SubroutineNotAllPathsReturnError", + span, + `Subroutine ${subroutineName}: not all code paths return a value`, + ); + +export const ThisCantBeReferencedInFunctionError = (span: Span) => + makeJackCompilerError( + "ThisCantBeReferencedInFunctionError", + span, + `this can't be referenced in a function`, + ); + +export const UndeclaredVariableError = (span: Span, variableName: string) => + makeJackCompilerError( + "UndeclaredVariableError", + span, + `Undeclared variable ${variableName}`, + ); + +export const UnknownClassError = (span: Span, className: string) => + makeJackCompilerError( + "UnknownClassError", + span, + `Class ${className} doesn't exist`, + ); + +export const UnknownSubroutineCallError = ( + span: Span, + subroutineName: string, + className?: string, +) => + makeJackCompilerError( + "UnknownSubroutineCallError", + span, + `Can't find subroutine '${subroutineName} in '${className ?? "(unknown)"}`, + ); + +export const UnreachableCodeError = (span: Span) => + makeJackCompilerError("UnreachableCodeError", span, `Unreachable code`); + +export const VoidSubroutineReturnsValueError = (span: Span) => + makeJackCompilerError( + "VoidSubroutineReturnsValueError", + span, + "Cannot return a value from a void subroutine", + ); + +export const WrongLiteralTypeError = ( + span: Span, + expectedTypeName: string, + actualTypeName: string, +) => + makeJackCompilerError( + "WrongLiteralTypeError", + span, + `Cannot assign ${actualTypeName} type to ${expectedTypeName}`, + ); diff --git a/simulator/src/jack/generated/JackLexer.interp b/simulator/src/jack/generated/JackLexer.interp new file mode 100644 index 000000000..c9f1c3796 --- /dev/null +++ b/simulator/src/jack/generated/JackLexer.interp @@ -0,0 +1,155 @@ +token literal names: +null +'class' +'constructor' +'function' +'method' +'field' +'static' +'var' +'int' +'char' +'boolean' +'void' +'let' +'do' +'if' +'else' +'while' +'return' +'{' +'}' +'(' +')' +'[' +']' +'.' +',' +';' +'=' +'+' +'-' +'*' +'/' +'&' +'|' +'~' +'<' +'>' +null +null +null +null +'true' +'false' +'null' +'this' +null +null + +token symbolic names: +null +CLASS +CONSTRUCTOR +FUNCTION +METHOD +FIELD +STATIC +VAR +INT +CHAR +BOOLEAN +VOID +LET +DO +IF +ELSE +WHILE +RETURN +LBRACE +RBRACE +LPAREN +RPAREN +LBRACKET +RBRACKET +DOT +COMMA +SEMICOLON +EQUALS +PLUS +MINUS +MUL +DIV +AND +OR +TILDE +LESS_THAN +GREATER_THAN +WHITESPACE +BLOCK_COMMENT +LINE_COMMENT +INTEGER_LITERAL +TRUE +FALSE +NULL_LITERAL +THIS_LITERAL +IDENTIFIER +STRING_LITERAL + +rule names: +CLASS +CONSTRUCTOR +FUNCTION +METHOD +FIELD +STATIC +VAR +INT +CHAR +BOOLEAN +VOID +LET +DO +IF +ELSE +WHILE +RETURN +LBRACE +RBRACE +LPAREN +RPAREN +LBRACKET +RBRACKET +DOT +COMMA +SEMICOLON +EQUALS +PLUS +MINUS +MUL +DIV +AND +OR +TILDE +LESS_THAN +GREATER_THAN +WHITESPACE +BLOCK_COMMENT +LINE_COMMENT +INTEGER_LITERAL +TRUE +FALSE +NULL_LITERAL +THIS_LITERAL +IDENTIFIER +STRING_LITERAL + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 46, 308, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 4, 36, 234, 8, 36, 11, 36, 12, 36, 235, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 5, 37, 244, 8, 37, 10, 37, 12, 37, 247, 9, 37, 1, 37, 1, 37, 1, 37, 3, 37, 252, 8, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 260, 8, 38, 10, 38, 12, 38, 263, 9, 38, 1, 38, 1, 38, 1, 39, 4, 39, 268, 8, 39, 11, 39, 12, 39, 269, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 5, 44, 295, 8, 44, 10, 44, 12, 44, 298, 9, 44, 1, 45, 1, 45, 5, 45, 302, 8, 45, 10, 45, 12, 45, 305, 9, 45, 1, 45, 1, 45, 1, 245, 0, 46, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 1, 0, 6, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, 1, 0, 48, 57, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 10, 10, 13, 13, 34, 34, 314, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 1, 93, 1, 0, 0, 0, 3, 99, 1, 0, 0, 0, 5, 111, 1, 0, 0, 0, 7, 120, 1, 0, 0, 0, 9, 127, 1, 0, 0, 0, 11, 133, 1, 0, 0, 0, 13, 140, 1, 0, 0, 0, 15, 144, 1, 0, 0, 0, 17, 148, 1, 0, 0, 0, 19, 153, 1, 0, 0, 0, 21, 161, 1, 0, 0, 0, 23, 166, 1, 0, 0, 0, 25, 170, 1, 0, 0, 0, 27, 173, 1, 0, 0, 0, 29, 176, 1, 0, 0, 0, 31, 181, 1, 0, 0, 0, 33, 187, 1, 0, 0, 0, 35, 194, 1, 0, 0, 0, 37, 196, 1, 0, 0, 0, 39, 198, 1, 0, 0, 0, 41, 200, 1, 0, 0, 0, 43, 202, 1, 0, 0, 0, 45, 204, 1, 0, 0, 0, 47, 206, 1, 0, 0, 0, 49, 208, 1, 0, 0, 0, 51, 210, 1, 0, 0, 0, 53, 212, 1, 0, 0, 0, 55, 214, 1, 0, 0, 0, 57, 216, 1, 0, 0, 0, 59, 218, 1, 0, 0, 0, 61, 220, 1, 0, 0, 0, 63, 222, 1, 0, 0, 0, 65, 224, 1, 0, 0, 0, 67, 226, 1, 0, 0, 0, 69, 228, 1, 0, 0, 0, 71, 230, 1, 0, 0, 0, 73, 233, 1, 0, 0, 0, 75, 239, 1, 0, 0, 0, 77, 255, 1, 0, 0, 0, 79, 267, 1, 0, 0, 0, 81, 271, 1, 0, 0, 0, 83, 276, 1, 0, 0, 0, 85, 282, 1, 0, 0, 0, 87, 287, 1, 0, 0, 0, 89, 292, 1, 0, 0, 0, 91, 299, 1, 0, 0, 0, 93, 94, 5, 99, 0, 0, 94, 95, 5, 108, 0, 0, 95, 96, 5, 97, 0, 0, 96, 97, 5, 115, 0, 0, 97, 98, 5, 115, 0, 0, 98, 2, 1, 0, 0, 0, 99, 100, 5, 99, 0, 0, 100, 101, 5, 111, 0, 0, 101, 102, 5, 110, 0, 0, 102, 103, 5, 115, 0, 0, 103, 104, 5, 116, 0, 0, 104, 105, 5, 114, 0, 0, 105, 106, 5, 117, 0, 0, 106, 107, 5, 99, 0, 0, 107, 108, 5, 116, 0, 0, 108, 109, 5, 111, 0, 0, 109, 110, 5, 114, 0, 0, 110, 4, 1, 0, 0, 0, 111, 112, 5, 102, 0, 0, 112, 113, 5, 117, 0, 0, 113, 114, 5, 110, 0, 0, 114, 115, 5, 99, 0, 0, 115, 116, 5, 116, 0, 0, 116, 117, 5, 105, 0, 0, 117, 118, 5, 111, 0, 0, 118, 119, 5, 110, 0, 0, 119, 6, 1, 0, 0, 0, 120, 121, 5, 109, 0, 0, 121, 122, 5, 101, 0, 0, 122, 123, 5, 116, 0, 0, 123, 124, 5, 104, 0, 0, 124, 125, 5, 111, 0, 0, 125, 126, 5, 100, 0, 0, 126, 8, 1, 0, 0, 0, 127, 128, 5, 102, 0, 0, 128, 129, 5, 105, 0, 0, 129, 130, 5, 101, 0, 0, 130, 131, 5, 108, 0, 0, 131, 132, 5, 100, 0, 0, 132, 10, 1, 0, 0, 0, 133, 134, 5, 115, 0, 0, 134, 135, 5, 116, 0, 0, 135, 136, 5, 97, 0, 0, 136, 137, 5, 116, 0, 0, 137, 138, 5, 105, 0, 0, 138, 139, 5, 99, 0, 0, 139, 12, 1, 0, 0, 0, 140, 141, 5, 118, 0, 0, 141, 142, 5, 97, 0, 0, 142, 143, 5, 114, 0, 0, 143, 14, 1, 0, 0, 0, 144, 145, 5, 105, 0, 0, 145, 146, 5, 110, 0, 0, 146, 147, 5, 116, 0, 0, 147, 16, 1, 0, 0, 0, 148, 149, 5, 99, 0, 0, 149, 150, 5, 104, 0, 0, 150, 151, 5, 97, 0, 0, 151, 152, 5, 114, 0, 0, 152, 18, 1, 0, 0, 0, 153, 154, 5, 98, 0, 0, 154, 155, 5, 111, 0, 0, 155, 156, 5, 111, 0, 0, 156, 157, 5, 108, 0, 0, 157, 158, 5, 101, 0, 0, 158, 159, 5, 97, 0, 0, 159, 160, 5, 110, 0, 0, 160, 20, 1, 0, 0, 0, 161, 162, 5, 118, 0, 0, 162, 163, 5, 111, 0, 0, 163, 164, 5, 105, 0, 0, 164, 165, 5, 100, 0, 0, 165, 22, 1, 0, 0, 0, 166, 167, 5, 108, 0, 0, 167, 168, 5, 101, 0, 0, 168, 169, 5, 116, 0, 0, 169, 24, 1, 0, 0, 0, 170, 171, 5, 100, 0, 0, 171, 172, 5, 111, 0, 0, 172, 26, 1, 0, 0, 0, 173, 174, 5, 105, 0, 0, 174, 175, 5, 102, 0, 0, 175, 28, 1, 0, 0, 0, 176, 177, 5, 101, 0, 0, 177, 178, 5, 108, 0, 0, 178, 179, 5, 115, 0, 0, 179, 180, 5, 101, 0, 0, 180, 30, 1, 0, 0, 0, 181, 182, 5, 119, 0, 0, 182, 183, 5, 104, 0, 0, 183, 184, 5, 105, 0, 0, 184, 185, 5, 108, 0, 0, 185, 186, 5, 101, 0, 0, 186, 32, 1, 0, 0, 0, 187, 188, 5, 114, 0, 0, 188, 189, 5, 101, 0, 0, 189, 190, 5, 116, 0, 0, 190, 191, 5, 117, 0, 0, 191, 192, 5, 114, 0, 0, 192, 193, 5, 110, 0, 0, 193, 34, 1, 0, 0, 0, 194, 195, 5, 123, 0, 0, 195, 36, 1, 0, 0, 0, 196, 197, 5, 125, 0, 0, 197, 38, 1, 0, 0, 0, 198, 199, 5, 40, 0, 0, 199, 40, 1, 0, 0, 0, 200, 201, 5, 41, 0, 0, 201, 42, 1, 0, 0, 0, 202, 203, 5, 91, 0, 0, 203, 44, 1, 0, 0, 0, 204, 205, 5, 93, 0, 0, 205, 46, 1, 0, 0, 0, 206, 207, 5, 46, 0, 0, 207, 48, 1, 0, 0, 0, 208, 209, 5, 44, 0, 0, 209, 50, 1, 0, 0, 0, 210, 211, 5, 59, 0, 0, 211, 52, 1, 0, 0, 0, 212, 213, 5, 61, 0, 0, 213, 54, 1, 0, 0, 0, 214, 215, 5, 43, 0, 0, 215, 56, 1, 0, 0, 0, 216, 217, 5, 45, 0, 0, 217, 58, 1, 0, 0, 0, 218, 219, 5, 42, 0, 0, 219, 60, 1, 0, 0, 0, 220, 221, 5, 47, 0, 0, 221, 62, 1, 0, 0, 0, 222, 223, 5, 38, 0, 0, 223, 64, 1, 0, 0, 0, 224, 225, 5, 124, 0, 0, 225, 66, 1, 0, 0, 0, 226, 227, 5, 126, 0, 0, 227, 68, 1, 0, 0, 0, 228, 229, 5, 60, 0, 0, 229, 70, 1, 0, 0, 0, 230, 231, 5, 62, 0, 0, 231, 72, 1, 0, 0, 0, 232, 234, 7, 0, 0, 0, 233, 232, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 238, 6, 36, 0, 0, 238, 74, 1, 0, 0, 0, 239, 240, 5, 47, 0, 0, 240, 241, 5, 42, 0, 0, 241, 245, 1, 0, 0, 0, 242, 244, 9, 0, 0, 0, 243, 242, 1, 0, 0, 0, 244, 247, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 251, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 248, 249, 5, 42, 0, 0, 249, 252, 5, 47, 0, 0, 250, 252, 5, 0, 0, 1, 251, 248, 1, 0, 0, 0, 251, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 254, 6, 37, 0, 0, 254, 76, 1, 0, 0, 0, 255, 256, 5, 47, 0, 0, 256, 257, 5, 47, 0, 0, 257, 261, 1, 0, 0, 0, 258, 260, 8, 1, 0, 0, 259, 258, 1, 0, 0, 0, 260, 263, 1, 0, 0, 0, 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 264, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 264, 265, 6, 38, 0, 0, 265, 78, 1, 0, 0, 0, 266, 268, 7, 2, 0, 0, 267, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 80, 1, 0, 0, 0, 271, 272, 5, 116, 0, 0, 272, 273, 5, 114, 0, 0, 273, 274, 5, 117, 0, 0, 274, 275, 5, 101, 0, 0, 275, 82, 1, 0, 0, 0, 276, 277, 5, 102, 0, 0, 277, 278, 5, 97, 0, 0, 278, 279, 5, 108, 0, 0, 279, 280, 5, 115, 0, 0, 280, 281, 5, 101, 0, 0, 281, 84, 1, 0, 0, 0, 282, 283, 5, 110, 0, 0, 283, 284, 5, 117, 0, 0, 284, 285, 5, 108, 0, 0, 285, 286, 5, 108, 0, 0, 286, 86, 1, 0, 0, 0, 287, 288, 5, 116, 0, 0, 288, 289, 5, 104, 0, 0, 289, 290, 5, 105, 0, 0, 290, 291, 5, 115, 0, 0, 291, 88, 1, 0, 0, 0, 292, 296, 7, 3, 0, 0, 293, 295, 7, 4, 0, 0, 294, 293, 1, 0, 0, 0, 295, 298, 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 90, 1, 0, 0, 0, 298, 296, 1, 0, 0, 0, 299, 303, 5, 34, 0, 0, 300, 302, 8, 5, 0, 0, 301, 300, 1, 0, 0, 0, 302, 305, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, 0, 304, 306, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 306, 307, 5, 34, 0, 0, 307, 92, 1, 0, 0, 0, 8, 0, 235, 245, 251, 261, 269, 296, 303, 1, 0, 1, 0] \ No newline at end of file diff --git a/simulator/src/jack/generated/JackLexer.tokens b/simulator/src/jack/generated/JackLexer.tokens new file mode 100644 index 000000000..7a0969b6a --- /dev/null +++ b/simulator/src/jack/generated/JackLexer.tokens @@ -0,0 +1,86 @@ +CLASS=1 +CONSTRUCTOR=2 +FUNCTION=3 +METHOD=4 +FIELD=5 +STATIC=6 +VAR=7 +INT=8 +CHAR=9 +BOOLEAN=10 +VOID=11 +LET=12 +DO=13 +IF=14 +ELSE=15 +WHILE=16 +RETURN=17 +LBRACE=18 +RBRACE=19 +LPAREN=20 +RPAREN=21 +LBRACKET=22 +RBRACKET=23 +DOT=24 +COMMA=25 +SEMICOLON=26 +EQUALS=27 +PLUS=28 +MINUS=29 +MUL=30 +DIV=31 +AND=32 +OR=33 +TILDE=34 +LESS_THAN=35 +GREATER_THAN=36 +WHITESPACE=37 +BLOCK_COMMENT=38 +LINE_COMMENT=39 +INTEGER_LITERAL=40 +TRUE=41 +FALSE=42 +NULL_LITERAL=43 +THIS_LITERAL=44 +IDENTIFIER=45 +STRING_LITERAL=46 +'class'=1 +'constructor'=2 +'function'=3 +'method'=4 +'field'=5 +'static'=6 +'var'=7 +'int'=8 +'char'=9 +'boolean'=10 +'void'=11 +'let'=12 +'do'=13 +'if'=14 +'else'=15 +'while'=16 +'return'=17 +'{'=18 +'}'=19 +'('=20 +')'=21 +'['=22 +']'=23 +'.'=24 +','=25 +';'=26 +'='=27 +'+'=28 +'-'=29 +'*'=30 +'/'=31 +'&'=32 +'|'=33 +'~'=34 +'<'=35 +'>'=36 +'true'=41 +'false'=42 +'null'=43 +'this'=44 diff --git a/simulator/src/jack/generated/JackLexer.ts b/simulator/src/jack/generated/JackLexer.ts new file mode 100644 index 000000000..e59c86334 --- /dev/null +++ b/simulator/src/jack/generated/JackLexer.ts @@ -0,0 +1,394 @@ +// Generated from JackLexer.g4 by ANTLR 4.13.1 + +import * as antlr from "antlr4ng"; + +export class JackLexer extends antlr.Lexer { + public static readonly CLASS = 1; + public static readonly CONSTRUCTOR = 2; + public static readonly FUNCTION = 3; + public static readonly METHOD = 4; + public static readonly FIELD = 5; + public static readonly STATIC = 6; + public static readonly VAR = 7; + public static readonly INT = 8; + public static readonly CHAR = 9; + public static readonly BOOLEAN = 10; + public static readonly VOID = 11; + public static readonly LET = 12; + public static readonly DO = 13; + public static readonly IF = 14; + public static readonly ELSE = 15; + public static readonly WHILE = 16; + public static readonly RETURN = 17; + public static readonly LBRACE = 18; + public static readonly RBRACE = 19; + public static readonly LPAREN = 20; + public static readonly RPAREN = 21; + public static readonly LBRACKET = 22; + public static readonly RBRACKET = 23; + public static readonly DOT = 24; + public static readonly COMMA = 25; + public static readonly SEMICOLON = 26; + public static readonly EQUALS = 27; + public static readonly PLUS = 28; + public static readonly MINUS = 29; + public static readonly MUL = 30; + public static readonly DIV = 31; + public static readonly AND = 32; + public static readonly OR = 33; + public static readonly TILDE = 34; + public static readonly LESS_THAN = 35; + public static readonly GREATER_THAN = 36; + public static readonly WHITESPACE = 37; + public static readonly BLOCK_COMMENT = 38; + public static readonly LINE_COMMENT = 39; + public static readonly INTEGER_LITERAL = 40; + public static readonly TRUE = 41; + public static readonly FALSE = 42; + public static readonly NULL_LITERAL = 43; + public static readonly THIS_LITERAL = 44; + public static readonly IDENTIFIER = 45; + public static readonly STRING_LITERAL = 46; + + public static readonly channelNames = ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"]; + + public static readonly literalNames = [ + null, + "'class'", + "'constructor'", + "'function'", + "'method'", + "'field'", + "'static'", + "'var'", + "'int'", + "'char'", + "'boolean'", + "'void'", + "'let'", + "'do'", + "'if'", + "'else'", + "'while'", + "'return'", + "'{'", + "'}'", + "'('", + "')'", + "'['", + "']'", + "'.'", + "','", + "';'", + "'='", + "'+'", + "'-'", + "'*'", + "'/'", + "'&'", + "'|'", + "'~'", + "'<'", + "'>'", + null, + null, + null, + null, + "'true'", + "'false'", + "'null'", + "'this'", + ]; + + public static readonly symbolicNames = [ + null, + "CLASS", + "CONSTRUCTOR", + "FUNCTION", + "METHOD", + "FIELD", + "STATIC", + "VAR", + "INT", + "CHAR", + "BOOLEAN", + "VOID", + "LET", + "DO", + "IF", + "ELSE", + "WHILE", + "RETURN", + "LBRACE", + "RBRACE", + "LPAREN", + "RPAREN", + "LBRACKET", + "RBRACKET", + "DOT", + "COMMA", + "SEMICOLON", + "EQUALS", + "PLUS", + "MINUS", + "MUL", + "DIV", + "AND", + "OR", + "TILDE", + "LESS_THAN", + "GREATER_THAN", + "WHITESPACE", + "BLOCK_COMMENT", + "LINE_COMMENT", + "INTEGER_LITERAL", + "TRUE", + "FALSE", + "NULL_LITERAL", + "THIS_LITERAL", + "IDENTIFIER", + "STRING_LITERAL", + ]; + + public static readonly modeNames = ["DEFAULT_MODE"]; + + public static readonly ruleNames = [ + "CLASS", + "CONSTRUCTOR", + "FUNCTION", + "METHOD", + "FIELD", + "STATIC", + "VAR", + "INT", + "CHAR", + "BOOLEAN", + "VOID", + "LET", + "DO", + "IF", + "ELSE", + "WHILE", + "RETURN", + "LBRACE", + "RBRACE", + "LPAREN", + "RPAREN", + "LBRACKET", + "RBRACKET", + "DOT", + "COMMA", + "SEMICOLON", + "EQUALS", + "PLUS", + "MINUS", + "MUL", + "DIV", + "AND", + "OR", + "TILDE", + "LESS_THAN", + "GREATER_THAN", + "WHITESPACE", + "BLOCK_COMMENT", + "LINE_COMMENT", + "INTEGER_LITERAL", + "TRUE", + "FALSE", + "NULL_LITERAL", + "THIS_LITERAL", + "IDENTIFIER", + "STRING_LITERAL", + ]; + + public constructor(input: antlr.CharStream) { + super(input); + this.interpreter = new antlr.LexerATNSimulator( + this, + JackLexer._ATN, + JackLexer.decisionsToDFA, + new antlr.PredictionContextCache(), + ); + } + + public get grammarFileName(): string { + return "JackLexer.g4"; + } + + public get literalNames(): (string | null)[] { + return JackLexer.literalNames; + } + public get symbolicNames(): (string | null)[] { + return JackLexer.symbolicNames; + } + public get ruleNames(): string[] { + return JackLexer.ruleNames; + } + + public get serializedATN(): number[] { + return JackLexer._serializedATN; + } + + public get channelNames(): string[] { + return JackLexer.channelNames; + } + + public get modeNames(): string[] { + return JackLexer.modeNames; + } + + public static readonly _serializedATN: number[] = [ + 4, 0, 46, 308, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, + 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, + 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, + 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, + 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, + 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, + 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, + 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, + 43, 2, 44, 7, 44, 2, 45, 7, 45, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, + 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, + 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, + 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, + 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, + 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, + 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, + 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, + 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, + 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, + 1, 34, 1, 35, 1, 35, 1, 36, 4, 36, 234, 8, 36, 11, 36, 12, 36, 235, 1, 36, + 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 5, 37, 244, 8, 37, 10, 37, 12, 37, 247, + 9, 37, 1, 37, 1, 37, 1, 37, 3, 37, 252, 8, 37, 1, 37, 1, 37, 1, 38, 1, 38, + 1, 38, 1, 38, 5, 38, 260, 8, 38, 10, 38, 12, 38, 263, 9, 38, 1, 38, 1, 38, + 1, 39, 4, 39, 268, 8, 39, 11, 39, 12, 39, 269, 1, 40, 1, 40, 1, 40, 1, 40, + 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, + 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 5, 44, 295, 8, 44, + 10, 44, 12, 44, 298, 9, 44, 1, 45, 1, 45, 5, 45, 302, 8, 45, 10, 45, 12, 45, + 305, 9, 45, 1, 45, 1, 45, 1, 245, 0, 46, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, + 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, + 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, + 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, + 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, + 89, 45, 91, 46, 1, 0, 6, 3, 0, 9, 10, 12, 13, 32, 32, 2, 0, 10, 10, 13, 13, + 1, 0, 48, 57, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, + 97, 122, 3, 0, 10, 10, 13, 13, 34, 34, 314, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, + 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, + 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, + 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, + 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, + 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, + 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, + 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, + 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, + 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, + 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, + 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, + 1, 93, 1, 0, 0, 0, 3, 99, 1, 0, 0, 0, 5, 111, 1, 0, 0, 0, 7, 120, 1, 0, 0, + 0, 9, 127, 1, 0, 0, 0, 11, 133, 1, 0, 0, 0, 13, 140, 1, 0, 0, 0, 15, 144, 1, + 0, 0, 0, 17, 148, 1, 0, 0, 0, 19, 153, 1, 0, 0, 0, 21, 161, 1, 0, 0, 0, 23, + 166, 1, 0, 0, 0, 25, 170, 1, 0, 0, 0, 27, 173, 1, 0, 0, 0, 29, 176, 1, 0, 0, + 0, 31, 181, 1, 0, 0, 0, 33, 187, 1, 0, 0, 0, 35, 194, 1, 0, 0, 0, 37, 196, + 1, 0, 0, 0, 39, 198, 1, 0, 0, 0, 41, 200, 1, 0, 0, 0, 43, 202, 1, 0, 0, 0, + 45, 204, 1, 0, 0, 0, 47, 206, 1, 0, 0, 0, 49, 208, 1, 0, 0, 0, 51, 210, 1, + 0, 0, 0, 53, 212, 1, 0, 0, 0, 55, 214, 1, 0, 0, 0, 57, 216, 1, 0, 0, 0, 59, + 218, 1, 0, 0, 0, 61, 220, 1, 0, 0, 0, 63, 222, 1, 0, 0, 0, 65, 224, 1, 0, 0, + 0, 67, 226, 1, 0, 0, 0, 69, 228, 1, 0, 0, 0, 71, 230, 1, 0, 0, 0, 73, 233, + 1, 0, 0, 0, 75, 239, 1, 0, 0, 0, 77, 255, 1, 0, 0, 0, 79, 267, 1, 0, 0, 0, + 81, 271, 1, 0, 0, 0, 83, 276, 1, 0, 0, 0, 85, 282, 1, 0, 0, 0, 87, 287, 1, + 0, 0, 0, 89, 292, 1, 0, 0, 0, 91, 299, 1, 0, 0, 0, 93, 94, 5, 99, 0, 0, 94, + 95, 5, 108, 0, 0, 95, 96, 5, 97, 0, 0, 96, 97, 5, 115, 0, 0, 97, 98, 5, 115, + 0, 0, 98, 2, 1, 0, 0, 0, 99, 100, 5, 99, 0, 0, 100, 101, 5, 111, 0, 0, 101, + 102, 5, 110, 0, 0, 102, 103, 5, 115, 0, 0, 103, 104, 5, 116, 0, 0, 104, 105, + 5, 114, 0, 0, 105, 106, 5, 117, 0, 0, 106, 107, 5, 99, 0, 0, 107, 108, 5, + 116, 0, 0, 108, 109, 5, 111, 0, 0, 109, 110, 5, 114, 0, 0, 110, 4, 1, 0, 0, + 0, 111, 112, 5, 102, 0, 0, 112, 113, 5, 117, 0, 0, 113, 114, 5, 110, 0, 0, + 114, 115, 5, 99, 0, 0, 115, 116, 5, 116, 0, 0, 116, 117, 5, 105, 0, 0, 117, + 118, 5, 111, 0, 0, 118, 119, 5, 110, 0, 0, 119, 6, 1, 0, 0, 0, 120, 121, 5, + 109, 0, 0, 121, 122, 5, 101, 0, 0, 122, 123, 5, 116, 0, 0, 123, 124, 5, 104, + 0, 0, 124, 125, 5, 111, 0, 0, 125, 126, 5, 100, 0, 0, 126, 8, 1, 0, 0, 0, + 127, 128, 5, 102, 0, 0, 128, 129, 5, 105, 0, 0, 129, 130, 5, 101, 0, 0, 130, + 131, 5, 108, 0, 0, 131, 132, 5, 100, 0, 0, 132, 10, 1, 0, 0, 0, 133, 134, 5, + 115, 0, 0, 134, 135, 5, 116, 0, 0, 135, 136, 5, 97, 0, 0, 136, 137, 5, 116, + 0, 0, 137, 138, 5, 105, 0, 0, 138, 139, 5, 99, 0, 0, 139, 12, 1, 0, 0, 0, + 140, 141, 5, 118, 0, 0, 141, 142, 5, 97, 0, 0, 142, 143, 5, 114, 0, 0, 143, + 14, 1, 0, 0, 0, 144, 145, 5, 105, 0, 0, 145, 146, 5, 110, 0, 0, 146, 147, 5, + 116, 0, 0, 147, 16, 1, 0, 0, 0, 148, 149, 5, 99, 0, 0, 149, 150, 5, 104, 0, + 0, 150, 151, 5, 97, 0, 0, 151, 152, 5, 114, 0, 0, 152, 18, 1, 0, 0, 0, 153, + 154, 5, 98, 0, 0, 154, 155, 5, 111, 0, 0, 155, 156, 5, 111, 0, 0, 156, 157, + 5, 108, 0, 0, 157, 158, 5, 101, 0, 0, 158, 159, 5, 97, 0, 0, 159, 160, 5, + 110, 0, 0, 160, 20, 1, 0, 0, 0, 161, 162, 5, 118, 0, 0, 162, 163, 5, 111, 0, + 0, 163, 164, 5, 105, 0, 0, 164, 165, 5, 100, 0, 0, 165, 22, 1, 0, 0, 0, 166, + 167, 5, 108, 0, 0, 167, 168, 5, 101, 0, 0, 168, 169, 5, 116, 0, 0, 169, 24, + 1, 0, 0, 0, 170, 171, 5, 100, 0, 0, 171, 172, 5, 111, 0, 0, 172, 26, 1, 0, + 0, 0, 173, 174, 5, 105, 0, 0, 174, 175, 5, 102, 0, 0, 175, 28, 1, 0, 0, 0, + 176, 177, 5, 101, 0, 0, 177, 178, 5, 108, 0, 0, 178, 179, 5, 115, 0, 0, 179, + 180, 5, 101, 0, 0, 180, 30, 1, 0, 0, 0, 181, 182, 5, 119, 0, 0, 182, 183, 5, + 104, 0, 0, 183, 184, 5, 105, 0, 0, 184, 185, 5, 108, 0, 0, 185, 186, 5, 101, + 0, 0, 186, 32, 1, 0, 0, 0, 187, 188, 5, 114, 0, 0, 188, 189, 5, 101, 0, 0, + 189, 190, 5, 116, 0, 0, 190, 191, 5, 117, 0, 0, 191, 192, 5, 114, 0, 0, 192, + 193, 5, 110, 0, 0, 193, 34, 1, 0, 0, 0, 194, 195, 5, 123, 0, 0, 195, 36, 1, + 0, 0, 0, 196, 197, 5, 125, 0, 0, 197, 38, 1, 0, 0, 0, 198, 199, 5, 40, 0, 0, + 199, 40, 1, 0, 0, 0, 200, 201, 5, 41, 0, 0, 201, 42, 1, 0, 0, 0, 202, 203, + 5, 91, 0, 0, 203, 44, 1, 0, 0, 0, 204, 205, 5, 93, 0, 0, 205, 46, 1, 0, 0, + 0, 206, 207, 5, 46, 0, 0, 207, 48, 1, 0, 0, 0, 208, 209, 5, 44, 0, 0, 209, + 50, 1, 0, 0, 0, 210, 211, 5, 59, 0, 0, 211, 52, 1, 0, 0, 0, 212, 213, 5, 61, + 0, 0, 213, 54, 1, 0, 0, 0, 214, 215, 5, 43, 0, 0, 215, 56, 1, 0, 0, 0, 216, + 217, 5, 45, 0, 0, 217, 58, 1, 0, 0, 0, 218, 219, 5, 42, 0, 0, 219, 60, 1, 0, + 0, 0, 220, 221, 5, 47, 0, 0, 221, 62, 1, 0, 0, 0, 222, 223, 5, 38, 0, 0, + 223, 64, 1, 0, 0, 0, 224, 225, 5, 124, 0, 0, 225, 66, 1, 0, 0, 0, 226, 227, + 5, 126, 0, 0, 227, 68, 1, 0, 0, 0, 228, 229, 5, 60, 0, 0, 229, 70, 1, 0, 0, + 0, 230, 231, 5, 62, 0, 0, 231, 72, 1, 0, 0, 0, 232, 234, 7, 0, 0, 0, 233, + 232, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 235, 236, 1, 0, + 0, 0, 236, 237, 1, 0, 0, 0, 237, 238, 6, 36, 0, 0, 238, 74, 1, 0, 0, 0, 239, + 240, 5, 47, 0, 0, 240, 241, 5, 42, 0, 0, 241, 245, 1, 0, 0, 0, 242, 244, 9, + 0, 0, 0, 243, 242, 1, 0, 0, 0, 244, 247, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, + 245, 243, 1, 0, 0, 0, 246, 251, 1, 0, 0, 0, 247, 245, 1, 0, 0, 0, 248, 249, + 5, 42, 0, 0, 249, 252, 5, 47, 0, 0, 250, 252, 5, 0, 0, 1, 251, 248, 1, 0, 0, + 0, 251, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 254, 6, 37, 0, 0, 254, + 76, 1, 0, 0, 0, 255, 256, 5, 47, 0, 0, 256, 257, 5, 47, 0, 0, 257, 261, 1, + 0, 0, 0, 258, 260, 8, 1, 0, 0, 259, 258, 1, 0, 0, 0, 260, 263, 1, 0, 0, 0, + 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 264, 1, 0, 0, 0, 263, 261, + 1, 0, 0, 0, 264, 265, 6, 38, 0, 0, 265, 78, 1, 0, 0, 0, 266, 268, 7, 2, 0, + 0, 267, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, + 270, 1, 0, 0, 0, 270, 80, 1, 0, 0, 0, 271, 272, 5, 116, 0, 0, 272, 273, 5, + 114, 0, 0, 273, 274, 5, 117, 0, 0, 274, 275, 5, 101, 0, 0, 275, 82, 1, 0, 0, + 0, 276, 277, 5, 102, 0, 0, 277, 278, 5, 97, 0, 0, 278, 279, 5, 108, 0, 0, + 279, 280, 5, 115, 0, 0, 280, 281, 5, 101, 0, 0, 281, 84, 1, 0, 0, 0, 282, + 283, 5, 110, 0, 0, 283, 284, 5, 117, 0, 0, 284, 285, 5, 108, 0, 0, 285, 286, + 5, 108, 0, 0, 286, 86, 1, 0, 0, 0, 287, 288, 5, 116, 0, 0, 288, 289, 5, 104, + 0, 0, 289, 290, 5, 105, 0, 0, 290, 291, 5, 115, 0, 0, 291, 88, 1, 0, 0, 0, + 292, 296, 7, 3, 0, 0, 293, 295, 7, 4, 0, 0, 294, 293, 1, 0, 0, 0, 295, 298, + 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 90, 1, 0, 0, 0, + 298, 296, 1, 0, 0, 0, 299, 303, 5, 34, 0, 0, 300, 302, 8, 5, 0, 0, 301, 300, + 1, 0, 0, 0, 302, 305, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 303, 304, 1, 0, 0, + 0, 304, 306, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 306, 307, 5, 34, 0, 0, 307, + 92, 1, 0, 0, 0, 8, 0, 235, 245, 251, 261, 269, 296, 303, 1, 0, 1, 0, + ]; + + private static __ATN: antlr.ATN; + public static get _ATN(): antlr.ATN { + if (!JackLexer.__ATN) { + JackLexer.__ATN = new antlr.ATNDeserializer().deserialize( + JackLexer._serializedATN, + ); + } + + return JackLexer.__ATN; + } + + private static readonly vocabulary = new antlr.Vocabulary( + JackLexer.literalNames, + JackLexer.symbolicNames, + [], + ); + + public override get vocabulary(): antlr.Vocabulary { + return JackLexer.vocabulary; + } + + private static readonly decisionsToDFA = JackLexer._ATN.decisionToState.map( + (ds: antlr.DecisionState, index: number) => new antlr.DFA(ds, index), + ); +} diff --git a/simulator/src/jack/generated/JackParser.interp b/simulator/src/jack/generated/JackParser.interp new file mode 100644 index 000000000..6eef0a1c0 --- /dev/null +++ b/simulator/src/jack/generated/JackParser.interp @@ -0,0 +1,146 @@ +token literal names: +null +'class' +'constructor' +'function' +'method' +'field' +'static' +'var' +'int' +'char' +'boolean' +'void' +'let' +'do' +'if' +'else' +'while' +'return' +'{' +'}' +'(' +')' +'[' +']' +'.' +',' +';' +'=' +'+' +'-' +'*' +'/' +'&' +'|' +'~' +'<' +'>' +null +null +null +null +'true' +'false' +'null' +'this' +null +null + +token symbolic names: +null +CLASS +CONSTRUCTOR +FUNCTION +METHOD +FIELD +STATIC +VAR +INT +CHAR +BOOLEAN +VOID +LET +DO +IF +ELSE +WHILE +RETURN +LBRACE +RBRACE +LPAREN +RPAREN +LBRACKET +RBRACKET +DOT +COMMA +SEMICOLON +EQUALS +PLUS +MINUS +MUL +DIV +AND +OR +TILDE +LESS_THAN +GREATER_THAN +WHITESPACE +BLOCK_COMMENT +LINE_COMMENT +INTEGER_LITERAL +TRUE +FALSE +NULL_LITERAL +THIS_LITERAL +IDENTIFIER +STRING_LITERAL + +rule names: +program +classDeclaration +className +classVarDec +fieldList +fieldName +subroutineDeclaration +subroutineType +subroutineDecWithoutType +subroutineName +subroutineReturnType +varType +parameterList +parameter +parameterName +subroutineBody +rBrace +varDeclaration +varNameInDeclaration +statements +statement +letStatement +equals +ifElseStatement +ifStatement +ifExpression +elseStatement +whileStatement +whileExpression +doStatement +subroutineCall +subroutineId +returnStatement +expressionList +expression +constant +varName +arrayAccess +unaryOperation +groupedExpression +booleanLiteral +unaryOperator +binaryOperator + + +atn: +[4, 1, 46, 317, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 94, 8, 1, 10, 1, 12, 1, 97, 9, 1, 1, 1, 5, 1, 100, 8, 1, 10, 1, 12, 1, 103, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 117, 8, 4, 10, 4, 12, 4, 120, 9, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 3, 10, 140, 8, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 147, 8, 12, 10, 12, 12, 12, 150, 9, 12, 3, 12, 152, 8, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 5, 15, 161, 8, 15, 10, 15, 12, 15, 164, 9, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 176, 8, 17, 10, 17, 12, 17, 179, 9, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 5, 19, 186, 8, 19, 10, 19, 12, 19, 189, 9, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 196, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 201, 8, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 3, 23, 211, 8, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 249, 8, 31, 1, 31, 3, 31, 252, 8, 31, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 258, 8, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 5, 33, 265, 8, 33, 10, 33, 12, 33, 268, 9, 33, 3, 33, 270, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 279, 8, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 285, 8, 34, 10, 34, 12, 34, 288, 9, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 295, 8, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 0, 1, 68, 43, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 0, 6, 1, 0, 5, 6, 1, 0, 2, 4, 2, 0, 8, 10, 45, 45, 1, 0, 41, 42, 2, 0, 29, 29, 34, 34, 2, 0, 27, 33, 35, 36, 303, 0, 86, 1, 0, 0, 0, 2, 89, 1, 0, 0, 0, 4, 106, 1, 0, 0, 0, 6, 108, 1, 0, 0, 0, 8, 112, 1, 0, 0, 0, 10, 121, 1, 0, 0, 0, 12, 123, 1, 0, 0, 0, 14, 126, 1, 0, 0, 0, 16, 128, 1, 0, 0, 0, 18, 135, 1, 0, 0, 0, 20, 139, 1, 0, 0, 0, 22, 141, 1, 0, 0, 0, 24, 151, 1, 0, 0, 0, 26, 153, 1, 0, 0, 0, 28, 156, 1, 0, 0, 0, 30, 158, 1, 0, 0, 0, 32, 168, 1, 0, 0, 0, 34, 170, 1, 0, 0, 0, 36, 182, 1, 0, 0, 0, 38, 187, 1, 0, 0, 0, 40, 195, 1, 0, 0, 0, 42, 197, 1, 0, 0, 0, 44, 206, 1, 0, 0, 0, 46, 208, 1, 0, 0, 0, 48, 212, 1, 0, 0, 0, 50, 220, 1, 0, 0, 0, 52, 222, 1, 0, 0, 0, 54, 227, 1, 0, 0, 0, 56, 235, 1, 0, 0, 0, 58, 237, 1, 0, 0, 0, 60, 241, 1, 0, 0, 0, 62, 251, 1, 0, 0, 0, 64, 255, 1, 0, 0, 0, 66, 269, 1, 0, 0, 0, 68, 278, 1, 0, 0, 0, 70, 294, 1, 0, 0, 0, 72, 296, 1, 0, 0, 0, 74, 298, 1, 0, 0, 0, 76, 303, 1, 0, 0, 0, 78, 306, 1, 0, 0, 0, 80, 310, 1, 0, 0, 0, 82, 312, 1, 0, 0, 0, 84, 314, 1, 0, 0, 0, 86, 87, 3, 2, 1, 0, 87, 88, 5, 0, 0, 1, 88, 1, 1, 0, 0, 0, 89, 90, 5, 1, 0, 0, 90, 91, 3, 4, 2, 0, 91, 95, 5, 18, 0, 0, 92, 94, 3, 6, 3, 0, 93, 92, 1, 0, 0, 0, 94, 97, 1, 0, 0, 0, 95, 93, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 101, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 98, 100, 3, 12, 6, 0, 99, 98, 1, 0, 0, 0, 100, 103, 1, 0, 0, 0, 101, 99, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 104, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 104, 105, 3, 32, 16, 0, 105, 3, 1, 0, 0, 0, 106, 107, 5, 45, 0, 0, 107, 5, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 3, 8, 4, 0, 110, 111, 5, 26, 0, 0, 111, 7, 1, 0, 0, 0, 112, 113, 3, 22, 11, 0, 113, 118, 3, 10, 5, 0, 114, 115, 5, 25, 0, 0, 115, 117, 3, 10, 5, 0, 116, 114, 1, 0, 0, 0, 117, 120, 1, 0, 0, 0, 118, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 9, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 121, 122, 5, 45, 0, 0, 122, 11, 1, 0, 0, 0, 123, 124, 3, 14, 7, 0, 124, 125, 3, 16, 8, 0, 125, 13, 1, 0, 0, 0, 126, 127, 7, 1, 0, 0, 127, 15, 1, 0, 0, 0, 128, 129, 3, 20, 10, 0, 129, 130, 3, 18, 9, 0, 130, 131, 5, 20, 0, 0, 131, 132, 3, 24, 12, 0, 132, 133, 5, 21, 0, 0, 133, 134, 3, 30, 15, 0, 134, 17, 1, 0, 0, 0, 135, 136, 5, 45, 0, 0, 136, 19, 1, 0, 0, 0, 137, 140, 3, 22, 11, 0, 138, 140, 5, 11, 0, 0, 139, 137, 1, 0, 0, 0, 139, 138, 1, 0, 0, 0, 140, 21, 1, 0, 0, 0, 141, 142, 7, 2, 0, 0, 142, 23, 1, 0, 0, 0, 143, 148, 3, 26, 13, 0, 144, 145, 5, 25, 0, 0, 145, 147, 3, 26, 13, 0, 146, 144, 1, 0, 0, 0, 147, 150, 1, 0, 0, 0, 148, 146, 1, 0, 0, 0, 148, 149, 1, 0, 0, 0, 149, 152, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 151, 143, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 25, 1, 0, 0, 0, 153, 154, 3, 22, 11, 0, 154, 155, 3, 28, 14, 0, 155, 27, 1, 0, 0, 0, 156, 157, 5, 45, 0, 0, 157, 29, 1, 0, 0, 0, 158, 162, 5, 18, 0, 0, 159, 161, 3, 34, 17, 0, 160, 159, 1, 0, 0, 0, 161, 164, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 162, 163, 1, 0, 0, 0, 163, 165, 1, 0, 0, 0, 164, 162, 1, 0, 0, 0, 165, 166, 3, 38, 19, 0, 166, 167, 3, 32, 16, 0, 167, 31, 1, 0, 0, 0, 168, 169, 5, 19, 0, 0, 169, 33, 1, 0, 0, 0, 170, 171, 5, 7, 0, 0, 171, 172, 3, 22, 11, 0, 172, 177, 3, 36, 18, 0, 173, 174, 5, 25, 0, 0, 174, 176, 3, 36, 18, 0, 175, 173, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 181, 5, 26, 0, 0, 181, 35, 1, 0, 0, 0, 182, 183, 5, 45, 0, 0, 183, 37, 1, 0, 0, 0, 184, 186, 3, 40, 20, 0, 185, 184, 1, 0, 0, 0, 186, 189, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 39, 1, 0, 0, 0, 189, 187, 1, 0, 0, 0, 190, 196, 3, 42, 21, 0, 191, 196, 3, 46, 23, 0, 192, 196, 3, 54, 27, 0, 193, 196, 3, 58, 29, 0, 194, 196, 3, 64, 32, 0, 195, 190, 1, 0, 0, 0, 195, 191, 1, 0, 0, 0, 195, 192, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 194, 1, 0, 0, 0, 196, 41, 1, 0, 0, 0, 197, 200, 5, 12, 0, 0, 198, 201, 3, 72, 36, 0, 199, 201, 3, 74, 37, 0, 200, 198, 1, 0, 0, 0, 200, 199, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 3, 44, 22, 0, 203, 204, 3, 68, 34, 0, 204, 205, 5, 26, 0, 0, 205, 43, 1, 0, 0, 0, 206, 207, 5, 27, 0, 0, 207, 45, 1, 0, 0, 0, 208, 210, 3, 48, 24, 0, 209, 211, 3, 52, 26, 0, 210, 209, 1, 0, 0, 0, 210, 211, 1, 0, 0, 0, 211, 47, 1, 0, 0, 0, 212, 213, 5, 14, 0, 0, 213, 214, 5, 20, 0, 0, 214, 215, 3, 50, 25, 0, 215, 216, 5, 21, 0, 0, 216, 217, 5, 18, 0, 0, 217, 218, 3, 38, 19, 0, 218, 219, 3, 32, 16, 0, 219, 49, 1, 0, 0, 0, 220, 221, 3, 68, 34, 0, 221, 51, 1, 0, 0, 0, 222, 223, 5, 15, 0, 0, 223, 224, 5, 18, 0, 0, 224, 225, 3, 38, 19, 0, 225, 226, 3, 32, 16, 0, 226, 53, 1, 0, 0, 0, 227, 228, 5, 16, 0, 0, 228, 229, 5, 20, 0, 0, 229, 230, 3, 56, 28, 0, 230, 231, 5, 21, 0, 0, 231, 232, 5, 18, 0, 0, 232, 233, 3, 38, 19, 0, 233, 234, 3, 32, 16, 0, 234, 55, 1, 0, 0, 0, 235, 236, 3, 68, 34, 0, 236, 57, 1, 0, 0, 0, 237, 238, 5, 13, 0, 0, 238, 239, 3, 60, 30, 0, 239, 240, 5, 26, 0, 0, 240, 59, 1, 0, 0, 0, 241, 242, 3, 62, 31, 0, 242, 243, 5, 20, 0, 0, 243, 244, 3, 66, 33, 0, 244, 245, 5, 21, 0, 0, 245, 61, 1, 0, 0, 0, 246, 249, 3, 4, 2, 0, 247, 249, 5, 44, 0, 0, 248, 246, 1, 0, 0, 0, 248, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 252, 5, 24, 0, 0, 251, 248, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 254, 3, 18, 9, 0, 254, 63, 1, 0, 0, 0, 255, 257, 5, 17, 0, 0, 256, 258, 3, 68, 34, 0, 257, 256, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 260, 5, 26, 0, 0, 260, 65, 1, 0, 0, 0, 261, 266, 3, 68, 34, 0, 262, 263, 5, 25, 0, 0, 263, 265, 3, 68, 34, 0, 264, 262, 1, 0, 0, 0, 265, 268, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 270, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 269, 261, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 67, 1, 0, 0, 0, 271, 272, 6, 34, -1, 0, 272, 279, 3, 70, 35, 0, 273, 279, 3, 72, 36, 0, 274, 279, 3, 60, 30, 0, 275, 279, 3, 74, 37, 0, 276, 279, 3, 76, 38, 0, 277, 279, 3, 78, 39, 0, 278, 271, 1, 0, 0, 0, 278, 273, 1, 0, 0, 0, 278, 274, 1, 0, 0, 0, 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 278, 277, 1, 0, 0, 0, 279, 286, 1, 0, 0, 0, 280, 281, 10, 2, 0, 0, 281, 282, 3, 84, 42, 0, 282, 283, 3, 68, 34, 3, 283, 285, 1, 0, 0, 0, 284, 280, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 69, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 295, 5, 40, 0, 0, 290, 295, 5, 46, 0, 0, 291, 295, 3, 80, 40, 0, 292, 295, 5, 43, 0, 0, 293, 295, 5, 44, 0, 0, 294, 289, 1, 0, 0, 0, 294, 290, 1, 0, 0, 0, 294, 291, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 294, 293, 1, 0, 0, 0, 295, 71, 1, 0, 0, 0, 296, 297, 5, 45, 0, 0, 297, 73, 1, 0, 0, 0, 298, 299, 3, 72, 36, 0, 299, 300, 5, 22, 0, 0, 300, 301, 3, 68, 34, 0, 301, 302, 5, 23, 0, 0, 302, 75, 1, 0, 0, 0, 303, 304, 3, 82, 41, 0, 304, 305, 3, 68, 34, 0, 305, 77, 1, 0, 0, 0, 306, 307, 5, 20, 0, 0, 307, 308, 3, 68, 34, 0, 308, 309, 5, 21, 0, 0, 309, 79, 1, 0, 0, 0, 310, 311, 7, 3, 0, 0, 311, 81, 1, 0, 0, 0, 312, 313, 7, 4, 0, 0, 313, 83, 1, 0, 0, 0, 314, 315, 7, 5, 0, 0, 315, 85, 1, 0, 0, 0, 20, 95, 101, 118, 139, 148, 151, 162, 177, 187, 195, 200, 210, 248, 251, 257, 266, 269, 278, 286, 294] \ No newline at end of file diff --git a/simulator/src/jack/generated/JackParser.tokens b/simulator/src/jack/generated/JackParser.tokens new file mode 100644 index 000000000..7a0969b6a --- /dev/null +++ b/simulator/src/jack/generated/JackParser.tokens @@ -0,0 +1,86 @@ +CLASS=1 +CONSTRUCTOR=2 +FUNCTION=3 +METHOD=4 +FIELD=5 +STATIC=6 +VAR=7 +INT=8 +CHAR=9 +BOOLEAN=10 +VOID=11 +LET=12 +DO=13 +IF=14 +ELSE=15 +WHILE=16 +RETURN=17 +LBRACE=18 +RBRACE=19 +LPAREN=20 +RPAREN=21 +LBRACKET=22 +RBRACKET=23 +DOT=24 +COMMA=25 +SEMICOLON=26 +EQUALS=27 +PLUS=28 +MINUS=29 +MUL=30 +DIV=31 +AND=32 +OR=33 +TILDE=34 +LESS_THAN=35 +GREATER_THAN=36 +WHITESPACE=37 +BLOCK_COMMENT=38 +LINE_COMMENT=39 +INTEGER_LITERAL=40 +TRUE=41 +FALSE=42 +NULL_LITERAL=43 +THIS_LITERAL=44 +IDENTIFIER=45 +STRING_LITERAL=46 +'class'=1 +'constructor'=2 +'function'=3 +'method'=4 +'field'=5 +'static'=6 +'var'=7 +'int'=8 +'char'=9 +'boolean'=10 +'void'=11 +'let'=12 +'do'=13 +'if'=14 +'else'=15 +'while'=16 +'return'=17 +'{'=18 +'}'=19 +'('=20 +')'=21 +'['=22 +']'=23 +'.'=24 +','=25 +';'=26 +'='=27 +'+'=28 +'-'=29 +'*'=30 +'/'=31 +'&'=32 +'|'=33 +'~'=34 +'<'=35 +'>'=36 +'true'=41 +'false'=42 +'null'=43 +'this'=44 diff --git a/simulator/src/jack/generated/JackParser.ts b/simulator/src/jack/generated/JackParser.ts new file mode 100644 index 000000000..7038ddac3 --- /dev/null +++ b/simulator/src/jack/generated/JackParser.ts @@ -0,0 +1,3372 @@ +// Generated from JackParser.g4 by ANTLR 4.13.1 + +import * as antlr from "antlr4ng"; + +import { SubroutineScope, LocalSymbolTable } from "../symbol.js"; +import { JackParserListener } from "./JackParserListener.js"; +import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; + +export class JackParser extends antlr.Parser { + public static readonly CLASS = 1; + public static readonly CONSTRUCTOR = 2; + public static readonly FUNCTION = 3; + public static readonly METHOD = 4; + public static readonly FIELD = 5; + public static readonly STATIC = 6; + public static readonly VAR = 7; + public static readonly INT = 8; + public static readonly CHAR = 9; + public static readonly BOOLEAN = 10; + public static readonly VOID = 11; + public static readonly LET = 12; + public static readonly DO = 13; + public static readonly IF = 14; + public static readonly ELSE = 15; + public static readonly WHILE = 16; + public static readonly RETURN = 17; + public static readonly LBRACE = 18; + public static readonly RBRACE = 19; + public static readonly LPAREN = 20; + public static readonly RPAREN = 21; + public static readonly LBRACKET = 22; + public static readonly RBRACKET = 23; + public static readonly DOT = 24; + public static readonly COMMA = 25; + public static readonly SEMICOLON = 26; + public static readonly EQUALS = 27; + public static readonly PLUS = 28; + public static readonly MINUS = 29; + public static readonly MUL = 30; + public static readonly DIV = 31; + public static readonly AND = 32; + public static readonly OR = 33; + public static readonly TILDE = 34; + public static readonly LESS_THAN = 35; + public static readonly GREATER_THAN = 36; + public static readonly WHITESPACE = 37; + public static readonly BLOCK_COMMENT = 38; + public static readonly LINE_COMMENT = 39; + public static readonly INTEGER_LITERAL = 40; + public static readonly TRUE = 41; + public static readonly FALSE = 42; + public static readonly NULL_LITERAL = 43; + public static readonly THIS_LITERAL = 44; + public static readonly IDENTIFIER = 45; + public static readonly STRING_LITERAL = 46; + public static readonly RULE_program = 0; + public static readonly RULE_classDeclaration = 1; + public static readonly RULE_className = 2; + public static readonly RULE_classVarDec = 3; + public static readonly RULE_fieldList = 4; + public static readonly RULE_fieldName = 5; + public static readonly RULE_subroutineDeclaration = 6; + public static readonly RULE_subroutineType = 7; + public static readonly RULE_subroutineDecWithoutType = 8; + public static readonly RULE_subroutineName = 9; + public static readonly RULE_subroutineReturnType = 10; + public static readonly RULE_varType = 11; + public static readonly RULE_parameterList = 12; + public static readonly RULE_parameter = 13; + public static readonly RULE_parameterName = 14; + public static readonly RULE_subroutineBody = 15; + public static readonly RULE_rBrace = 16; + public static readonly RULE_varDeclaration = 17; + public static readonly RULE_varNameInDeclaration = 18; + public static readonly RULE_statements = 19; + public static readonly RULE_statement = 20; + public static readonly RULE_letStatement = 21; + public static readonly RULE_equals = 22; + public static readonly RULE_ifElseStatement = 23; + public static readonly RULE_ifStatement = 24; + public static readonly RULE_ifExpression = 25; + public static readonly RULE_elseStatement = 26; + public static readonly RULE_whileStatement = 27; + public static readonly RULE_whileExpression = 28; + public static readonly RULE_doStatement = 29; + public static readonly RULE_subroutineCall = 30; + public static readonly RULE_subroutineId = 31; + public static readonly RULE_returnStatement = 32; + public static readonly RULE_expressionList = 33; + public static readonly RULE_expression = 34; + public static readonly RULE_constant = 35; + public static readonly RULE_varName = 36; + public static readonly RULE_arrayAccess = 37; + public static readonly RULE_unaryOperation = 38; + public static readonly RULE_groupedExpression = 39; + public static readonly RULE_booleanLiteral = 40; + public static readonly RULE_unaryOperator = 41; + public static readonly RULE_binaryOperator = 42; + + public static readonly literalNames = [ + null, + "'class'", + "'constructor'", + "'function'", + "'method'", + "'field'", + "'static'", + "'var'", + "'int'", + "'char'", + "'boolean'", + "'void'", + "'let'", + "'do'", + "'if'", + "'else'", + "'while'", + "'return'", + "'{'", + "'}'", + "'('", + "')'", + "'['", + "']'", + "'.'", + "','", + "';'", + "'='", + "'+'", + "'-'", + "'*'", + "'/'", + "'&'", + "'|'", + "'~'", + "'<'", + "'>'", + null, + null, + null, + null, + "'true'", + "'false'", + "'null'", + "'this'", + ]; + + public static readonly symbolicNames = [ + null, + "CLASS", + "CONSTRUCTOR", + "FUNCTION", + "METHOD", + "FIELD", + "STATIC", + "VAR", + "INT", + "CHAR", + "BOOLEAN", + "VOID", + "LET", + "DO", + "IF", + "ELSE", + "WHILE", + "RETURN", + "LBRACE", + "RBRACE", + "LPAREN", + "RPAREN", + "LBRACKET", + "RBRACKET", + "DOT", + "COMMA", + "SEMICOLON", + "EQUALS", + "PLUS", + "MINUS", + "MUL", + "DIV", + "AND", + "OR", + "TILDE", + "LESS_THAN", + "GREATER_THAN", + "WHITESPACE", + "BLOCK_COMMENT", + "LINE_COMMENT", + "INTEGER_LITERAL", + "TRUE", + "FALSE", + "NULL_LITERAL", + "THIS_LITERAL", + "IDENTIFIER", + "STRING_LITERAL", + ]; + public static readonly ruleNames = [ + "program", + "classDeclaration", + "className", + "classVarDec", + "fieldList", + "fieldName", + "subroutineDeclaration", + "subroutineType", + "subroutineDecWithoutType", + "subroutineName", + "subroutineReturnType", + "varType", + "parameterList", + "parameter", + "parameterName", + "subroutineBody", + "rBrace", + "varDeclaration", + "varNameInDeclaration", + "statements", + "statement", + "letStatement", + "equals", + "ifElseStatement", + "ifStatement", + "ifExpression", + "elseStatement", + "whileStatement", + "whileExpression", + "doStatement", + "subroutineCall", + "subroutineId", + "returnStatement", + "expressionList", + "expression", + "constant", + "varName", + "arrayAccess", + "unaryOperation", + "groupedExpression", + "booleanLiteral", + "unaryOperator", + "binaryOperator", + ]; + + public get grammarFileName(): string { + return "JackParser.g4"; + } + public get literalNames(): (string | null)[] { + return JackParser.literalNames; + } + public get symbolicNames(): (string | null)[] { + return JackParser.symbolicNames; + } + public get ruleNames(): string[] { + return JackParser.ruleNames; + } + public get serializedATN(): number[] { + return JackParser._serializedATN; + } + + protected createFailedPredicateException( + predicate?: string, + message?: string, + ): antlr.FailedPredicateException { + return new antlr.FailedPredicateException(this, predicate, message); + } + + public constructor(input: antlr.TokenStream) { + super(input); + this.interpreter = new antlr.ParserATNSimulator( + this, + JackParser._ATN, + JackParser.decisionsToDFA, + new antlr.PredictionContextCache(), + ); + } + public program(): ProgramContext { + const localContext = new ProgramContext(this.context, this.state); + this.enterRule(localContext, 0, JackParser.RULE_program); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 86; + this.classDeclaration(); + this.state = 87; + this.match(JackParser.EOF); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public classDeclaration(): ClassDeclarationContext { + const localContext = new ClassDeclarationContext(this.context, this.state); + this.enterRule(localContext, 2, JackParser.RULE_classDeclaration); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 89; + this.match(JackParser.CLASS); + this.state = 90; + this.className(); + this.state = 91; + this.match(JackParser.LBRACE); + this.state = 95; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 5 || _la === 6) { + { + { + this.state = 92; + this.classVarDec(); + } + } + this.state = 97; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 101; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while ((_la & ~0x1f) === 0 && ((1 << _la) & 28) !== 0) { + { + { + this.state = 98; + this.subroutineDeclaration(); + } + } + this.state = 103; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 104; + this.rBrace(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public className(): ClassNameContext { + const localContext = new ClassNameContext(this.context, this.state); + this.enterRule(localContext, 4, JackParser.RULE_className); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 106; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public classVarDec(): ClassVarDecContext { + const localContext = new ClassVarDecContext(this.context, this.state); + this.enterRule(localContext, 6, JackParser.RULE_classVarDec); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 108; + _la = this.tokenStream.LA(1); + if (!(_la === 5 || _la === 6)) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + this.state = 109; + this.fieldList(); + this.state = 110; + this.match(JackParser.SEMICOLON); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public fieldList(): FieldListContext { + const localContext = new FieldListContext(this.context, this.state); + this.enterRule(localContext, 8, JackParser.RULE_fieldList); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 112; + this.varType(); + this.state = 113; + this.fieldName(); + this.state = 118; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 25) { + { + { + this.state = 114; + this.match(JackParser.COMMA); + this.state = 115; + this.fieldName(); + } + } + this.state = 120; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public fieldName(): FieldNameContext { + const localContext = new FieldNameContext(this.context, this.state); + this.enterRule(localContext, 10, JackParser.RULE_fieldName); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 121; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineDeclaration(): SubroutineDeclarationContext { + const localContext = new SubroutineDeclarationContext( + this.context, + this.state, + ); + this.enterRule(localContext, 12, JackParser.RULE_subroutineDeclaration); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 123; + this.subroutineType(); + this.state = 124; + this.subroutineDecWithoutType(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineType(): SubroutineTypeContext { + const localContext = new SubroutineTypeContext(this.context, this.state); + this.enterRule(localContext, 14, JackParser.RULE_subroutineType); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 126; + _la = this.tokenStream.LA(1); + if (!((_la & ~0x1f) === 0 && ((1 << _la) & 28) !== 0)) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineDecWithoutType(): SubroutineDecWithoutTypeContext { + const localContext = new SubroutineDecWithoutTypeContext( + this.context, + this.state, + ); + this.enterRule(localContext, 16, JackParser.RULE_subroutineDecWithoutType); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 128; + this.subroutineReturnType(); + this.state = 129; + this.subroutineName(); + this.state = 130; + this.match(JackParser.LPAREN); + this.state = 131; + this.parameterList(); + this.state = 132; + this.match(JackParser.RPAREN); + this.state = 133; + this.subroutineBody(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineName(): SubroutineNameContext { + const localContext = new SubroutineNameContext(this.context, this.state); + this.enterRule(localContext, 18, JackParser.RULE_subroutineName); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 135; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineReturnType(): SubroutineReturnTypeContext { + const localContext = new SubroutineReturnTypeContext( + this.context, + this.state, + ); + this.enterRule(localContext, 20, JackParser.RULE_subroutineReturnType); + try { + this.state = 139; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case JackParser.INT: + case JackParser.CHAR: + case JackParser.BOOLEAN: + case JackParser.IDENTIFIER: + this.enterOuterAlt(localContext, 1); + { + this.state = 137; + this.varType(); + } + break; + case JackParser.VOID: + this.enterOuterAlt(localContext, 2); + { + this.state = 138; + this.match(JackParser.VOID); + } + break; + default: + throw new antlr.NoViableAltException(this); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public varType(): VarTypeContext { + const localContext = new VarTypeContext(this.context, this.state); + this.enterRule(localContext, 22, JackParser.RULE_varType); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 141; + _la = this.tokenStream.LA(1); + if ( + !(((_la & ~0x1f) === 0 && ((1 << _la) & 1792) !== 0) || _la === 45) + ) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public parameterList(): ParameterListContext { + const localContext = new ParameterListContext(this.context, this.state); + this.enterRule(localContext, 24, JackParser.RULE_parameterList); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 151; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (((_la & ~0x1f) === 0 && ((1 << _la) & 1792) !== 0) || _la === 45) { + { + this.state = 143; + this.parameter(); + this.state = 148; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 25) { + { + { + this.state = 144; + this.match(JackParser.COMMA); + this.state = 145; + this.parameter(); + } + } + this.state = 150; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public parameter(): ParameterContext { + const localContext = new ParameterContext(this.context, this.state); + this.enterRule(localContext, 26, JackParser.RULE_parameter); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 153; + this.varType(); + this.state = 154; + this.parameterName(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public parameterName(): ParameterNameContext { + const localContext = new ParameterNameContext(this.context, this.state); + this.enterRule(localContext, 28, JackParser.RULE_parameterName); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 156; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineBody(): SubroutineBodyContext { + const localContext = new SubroutineBodyContext(this.context, this.state); + this.enterRule(localContext, 30, JackParser.RULE_subroutineBody); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 158; + this.match(JackParser.LBRACE); + this.state = 162; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 7) { + { + { + this.state = 159; + this.varDeclaration(); + } + } + this.state = 164; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 165; + this.statements(); + this.state = 166; + this.rBrace(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public rBrace(): RBraceContext { + const localContext = new RBraceContext(this.context, this.state); + this.enterRule(localContext, 32, JackParser.RULE_rBrace); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 168; + this.match(JackParser.RBRACE); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public varDeclaration(): VarDeclarationContext { + const localContext = new VarDeclarationContext(this.context, this.state); + this.enterRule(localContext, 34, JackParser.RULE_varDeclaration); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 170; + this.match(JackParser.VAR); + this.state = 171; + this.varType(); + this.state = 172; + this.varNameInDeclaration(); + this.state = 177; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 25) { + { + { + this.state = 173; + this.match(JackParser.COMMA); + this.state = 174; + this.varNameInDeclaration(); + } + } + this.state = 179; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + this.state = 180; + this.match(JackParser.SEMICOLON); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public varNameInDeclaration(): VarNameInDeclarationContext { + const localContext = new VarNameInDeclarationContext( + this.context, + this.state, + ); + this.enterRule(localContext, 36, JackParser.RULE_varNameInDeclaration); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 182; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public statements(): StatementsContext { + const localContext = new StatementsContext(this.context, this.state); + this.enterRule(localContext, 38, JackParser.RULE_statements); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 187; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while ((_la & ~0x1f) === 0 && ((1 << _la) & 225280) !== 0) { + { + { + this.state = 184; + this.statement(); + } + } + this.state = 189; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public statement(): StatementContext { + const localContext = new StatementContext(this.context, this.state); + this.enterRule(localContext, 40, JackParser.RULE_statement); + try { + this.state = 195; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case JackParser.LET: + this.enterOuterAlt(localContext, 1); + { + this.state = 190; + this.letStatement(); + } + break; + case JackParser.IF: + this.enterOuterAlt(localContext, 2); + { + this.state = 191; + this.ifElseStatement(); + } + break; + case JackParser.WHILE: + this.enterOuterAlt(localContext, 3); + { + this.state = 192; + this.whileStatement(); + } + break; + case JackParser.DO: + this.enterOuterAlt(localContext, 4); + { + this.state = 193; + this.doStatement(); + } + break; + case JackParser.RETURN: + this.enterOuterAlt(localContext, 5); + { + this.state = 194; + this.returnStatement(); + } + break; + default: + throw new antlr.NoViableAltException(this); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public letStatement(): LetStatementContext { + const localContext = new LetStatementContext(this.context, this.state); + this.enterRule(localContext, 42, JackParser.RULE_letStatement); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 197; + this.match(JackParser.LET); + this.state = 200; + this.errorHandler.sync(this); + switch ( + this.interpreter.adaptivePredict(this.tokenStream, 10, this.context) + ) { + case 1: + { + this.state = 198; + this.varName(); + } + break; + case 2: + { + this.state = 199; + this.arrayAccess(); + } + break; + } + this.state = 202; + this.equals(); + this.state = 203; + this.expression(0); + this.state = 204; + this.match(JackParser.SEMICOLON); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public equals(): EqualsContext { + const localContext = new EqualsContext(this.context, this.state); + this.enterRule(localContext, 44, JackParser.RULE_equals); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 206; + this.match(JackParser.EQUALS); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public ifElseStatement(): IfElseStatementContext { + const localContext = new IfElseStatementContext(this.context, this.state); + this.enterRule(localContext, 46, JackParser.RULE_ifElseStatement); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 208; + this.ifStatement(); + this.state = 210; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if (_la === 15) { + { + this.state = 209; + this.elseStatement(); + } + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public ifStatement(): IfStatementContext { + const localContext = new IfStatementContext(this.context, this.state); + this.enterRule(localContext, 48, JackParser.RULE_ifStatement); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 212; + this.match(JackParser.IF); + this.state = 213; + this.match(JackParser.LPAREN); + this.state = 214; + this.ifExpression(); + this.state = 215; + this.match(JackParser.RPAREN); + this.state = 216; + this.match(JackParser.LBRACE); + this.state = 217; + this.statements(); + this.state = 218; + this.rBrace(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public ifExpression(): IfExpressionContext { + const localContext = new IfExpressionContext(this.context, this.state); + this.enterRule(localContext, 50, JackParser.RULE_ifExpression); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 220; + this.expression(0); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public elseStatement(): ElseStatementContext { + const localContext = new ElseStatementContext(this.context, this.state); + this.enterRule(localContext, 52, JackParser.RULE_elseStatement); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 222; + this.match(JackParser.ELSE); + this.state = 223; + this.match(JackParser.LBRACE); + this.state = 224; + this.statements(); + this.state = 225; + this.rBrace(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public whileStatement(): WhileStatementContext { + const localContext = new WhileStatementContext(this.context, this.state); + this.enterRule(localContext, 54, JackParser.RULE_whileStatement); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 227; + this.match(JackParser.WHILE); + this.state = 228; + this.match(JackParser.LPAREN); + this.state = 229; + this.whileExpression(); + this.state = 230; + this.match(JackParser.RPAREN); + this.state = 231; + this.match(JackParser.LBRACE); + this.state = 232; + this.statements(); + this.state = 233; + this.rBrace(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public whileExpression(): WhileExpressionContext { + const localContext = new WhileExpressionContext(this.context, this.state); + this.enterRule(localContext, 56, JackParser.RULE_whileExpression); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 235; + this.expression(0); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public doStatement(): DoStatementContext { + const localContext = new DoStatementContext(this.context, this.state); + this.enterRule(localContext, 58, JackParser.RULE_doStatement); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 237; + this.match(JackParser.DO); + this.state = 238; + this.subroutineCall(); + this.state = 239; + this.match(JackParser.SEMICOLON); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineCall(): SubroutineCallContext { + const localContext = new SubroutineCallContext(this.context, this.state); + this.enterRule(localContext, 60, JackParser.RULE_subroutineCall); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 241; + this.subroutineId(); + this.state = 242; + this.match(JackParser.LPAREN); + this.state = 243; + this.expressionList(); + this.state = 244; + this.match(JackParser.RPAREN); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public subroutineId(): SubroutineIdContext { + const localContext = new SubroutineIdContext(this.context, this.state); + this.enterRule(localContext, 62, JackParser.RULE_subroutineId); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 251; + this.errorHandler.sync(this); + switch ( + this.interpreter.adaptivePredict(this.tokenStream, 13, this.context) + ) { + case 1: + { + this.state = 248; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case JackParser.IDENTIFIER: + { + this.state = 246; + this.className(); + } + break; + case JackParser.THIS_LITERAL: + { + this.state = 247; + this.match(JackParser.THIS_LITERAL); + } + break; + default: + throw new antlr.NoViableAltException(this); + } + this.state = 250; + this.match(JackParser.DOT); + } + break; + } + this.state = 253; + this.subroutineName(); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public returnStatement(): ReturnStatementContext { + const localContext = new ReturnStatementContext(this.context, this.state); + this.enterRule(localContext, 64, JackParser.RULE_returnStatement); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 255; + this.match(JackParser.RETURN); + this.state = 257; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if ( + ((_la - 20) & ~0x1f) === 0 && + ((1 << (_la - 20)) & 133186049) !== 0 + ) { + { + this.state = 256; + this.expression(0); + } + } + + this.state = 259; + this.match(JackParser.SEMICOLON); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public expressionList(): ExpressionListContext { + const localContext = new ExpressionListContext(this.context, this.state); + this.enterRule(localContext, 66, JackParser.RULE_expressionList); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 269; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + if ( + ((_la - 20) & ~0x1f) === 0 && + ((1 << (_la - 20)) & 133186049) !== 0 + ) { + { + this.state = 261; + this.expression(0); + this.state = 266; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + while (_la === 25) { + { + { + this.state = 262; + this.match(JackParser.COMMA); + this.state = 263; + this.expression(0); + } + } + this.state = 268; + this.errorHandler.sync(this); + _la = this.tokenStream.LA(1); + } + } + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + + public expression(): ExpressionContext; + public expression(_p: number): ExpressionContext; + public expression(_p?: number): ExpressionContext { + if (_p === undefined) { + _p = 0; + } + + const parentContext = this.context; + const parentState = this.state; + let localContext = new ExpressionContext(this.context, parentState); + const _startState = 68; + this.enterRecursionRule(localContext, 68, JackParser.RULE_expression, _p); + try { + let alternative: number; + this.enterOuterAlt(localContext, 1); + { + this.state = 278; + this.errorHandler.sync(this); + switch ( + this.interpreter.adaptivePredict(this.tokenStream, 17, this.context) + ) { + case 1: + { + this.state = 272; + this.constant(); + } + break; + case 2: + { + this.state = 273; + this.varName(); + } + break; + case 3: + { + this.state = 274; + this.subroutineCall(); + } + break; + case 4: + { + this.state = 275; + this.arrayAccess(); + } + break; + case 5: + { + this.state = 276; + this.unaryOperation(); + } + break; + case 6: + { + this.state = 277; + this.groupedExpression(); + } + break; + } + assertExists(this.context).stop = this.tokenStream.LT(-1); + this.state = 286; + this.errorHandler.sync(this); + alternative = this.interpreter.adaptivePredict( + this.tokenStream, + 18, + this.context, + ); + while ( + alternative !== 2 && + alternative !== antlr.ATN.INVALID_ALT_NUMBER + ) { + if (alternative === 1) { + if (this.parseListeners != null) { + this.triggerExitRuleEvent(); + } + { + { + localContext = new ExpressionContext( + parentContext, + parentState, + ); + this.pushNewRecursionContext( + localContext, + _startState, + JackParser.RULE_expression, + ); + this.state = 280; + if (!this.precpred(this.context, 2)) { + throw this.createFailedPredicateException( + "this.precpred(this.context, 2)", + ); + } + this.state = 281; + this.binaryOperator(); + this.state = 282; + this.expression(3); + } + } + } + this.state = 288; + this.errorHandler.sync(this); + alternative = this.interpreter.adaptivePredict( + this.tokenStream, + 18, + this.context, + ); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.unrollRecursionContexts(parentContext); + } + return localContext; + } + public constant(): ConstantContext { + const localContext = new ConstantContext(this.context, this.state); + this.enterRule(localContext, 70, JackParser.RULE_constant); + try { + this.state = 294; + this.errorHandler.sync(this); + switch (this.tokenStream.LA(1)) { + case JackParser.INTEGER_LITERAL: + this.enterOuterAlt(localContext, 1); + { + this.state = 289; + this.match(JackParser.INTEGER_LITERAL); + } + break; + case JackParser.STRING_LITERAL: + this.enterOuterAlt(localContext, 2); + { + this.state = 290; + this.match(JackParser.STRING_LITERAL); + } + break; + case JackParser.TRUE: + case JackParser.FALSE: + this.enterOuterAlt(localContext, 3); + { + this.state = 291; + this.booleanLiteral(); + } + break; + case JackParser.NULL_LITERAL: + this.enterOuterAlt(localContext, 4); + { + this.state = 292; + this.match(JackParser.NULL_LITERAL); + } + break; + case JackParser.THIS_LITERAL: + this.enterOuterAlt(localContext, 5); + { + this.state = 293; + this.match(JackParser.THIS_LITERAL); + } + break; + default: + throw new antlr.NoViableAltException(this); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public varName(): VarNameContext { + const localContext = new VarNameContext(this.context, this.state); + this.enterRule(localContext, 72, JackParser.RULE_varName); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 296; + this.match(JackParser.IDENTIFIER); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public arrayAccess(): ArrayAccessContext { + const localContext = new ArrayAccessContext(this.context, this.state); + this.enterRule(localContext, 74, JackParser.RULE_arrayAccess); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 298; + this.varName(); + this.state = 299; + this.match(JackParser.LBRACKET); + this.state = 300; + this.expression(0); + this.state = 301; + this.match(JackParser.RBRACKET); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public unaryOperation(): UnaryOperationContext { + const localContext = new UnaryOperationContext(this.context, this.state); + this.enterRule(localContext, 76, JackParser.RULE_unaryOperation); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 303; + this.unaryOperator(); + this.state = 304; + this.expression(0); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public groupedExpression(): GroupedExpressionContext { + const localContext = new GroupedExpressionContext(this.context, this.state); + this.enterRule(localContext, 78, JackParser.RULE_groupedExpression); + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 306; + this.match(JackParser.LPAREN); + this.state = 307; + this.expression(0); + this.state = 308; + this.match(JackParser.RPAREN); + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public booleanLiteral(): BooleanLiteralContext { + const localContext = new BooleanLiteralContext(this.context, this.state); + this.enterRule(localContext, 80, JackParser.RULE_booleanLiteral); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 310; + _la = this.tokenStream.LA(1); + if (!(_la === 41 || _la === 42)) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public unaryOperator(): UnaryOperatorContext { + const localContext = new UnaryOperatorContext(this.context, this.state); + this.enterRule(localContext, 82, JackParser.RULE_unaryOperator); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 312; + _la = this.tokenStream.LA(1); + if (!(_la === 29 || _la === 34)) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + public binaryOperator(): BinaryOperatorContext { + const localContext = new BinaryOperatorContext(this.context, this.state); + this.enterRule(localContext, 84, JackParser.RULE_binaryOperator); + let _la: number; + try { + this.enterOuterAlt(localContext, 1); + { + this.state = 314; + _la = this.tokenStream.LA(1); + if (!(((_la - 27) & ~0x1f) === 0 && ((1 << (_la - 27)) & 895) !== 0)) { + this.errorHandler.recoverInline(this); + } else { + this.errorHandler.reportMatch(this); + this.consume(); + } + } + } catch (re) { + if (re instanceof antlr.RecognitionException) { + this.errorHandler.reportError(this, re); + this.errorHandler.recover(this, re); + } else { + throw re; + } + } finally { + this.exitRule(); + } + return localContext; + } + + public override sempred( + localContext: antlr.ParserRuleContext | null, + ruleIndex: number, + predIndex: number, + ): boolean { + switch (ruleIndex) { + case 34: + return this.expression_sempred( + localContext as ExpressionContext, + predIndex, + ); + } + return true; + } + private expression_sempred( + localContext: ExpressionContext | null, + predIndex: number, + ): boolean { + switch (predIndex) { + case 0: + return this.precpred(this.context, 2); + } + return true; + } + + public static readonly _serializedATN: number[] = [ + 4, 1, 46, 317, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, + 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, + 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, + 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, + 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, + 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, + 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, + 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 1, 0, 1, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 94, 8, 1, 10, 1, 12, 1, 97, 9, 1, 1, 1, 5, + 1, 100, 8, 1, 10, 1, 12, 1, 103, 9, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 117, 8, 4, 10, 4, 12, 4, 120, 9, + 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, + 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 3, 10, 140, 8, 10, 1, 11, 1, 11, 1, + 12, 1, 12, 1, 12, 5, 12, 147, 8, 12, 10, 12, 12, 12, 150, 9, 12, 3, 12, 152, + 8, 12, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 5, 15, 161, 8, 15, + 10, 15, 12, 15, 164, 9, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, + 1, 17, 1, 17, 1, 17, 5, 17, 176, 8, 17, 10, 17, 12, 17, 179, 9, 17, 1, 17, + 1, 17, 1, 18, 1, 18, 1, 19, 5, 19, 186, 8, 19, 10, 19, 12, 19, 189, 9, 19, + 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 196, 8, 20, 1, 21, 1, 21, 1, 21, + 3, 21, 201, 8, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, + 3, 23, 211, 8, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, + 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, + 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, + 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 249, 8, 31, 1, 31, 3, 31, + 252, 8, 31, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 258, 8, 32, 1, 32, 1, 32, 1, + 33, 1, 33, 1, 33, 5, 33, 265, 8, 33, 10, 33, 12, 33, 268, 9, 33, 3, 33, 270, + 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 279, 8, 34, + 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 285, 8, 34, 10, 34, 12, 34, 288, 9, 34, + 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 295, 8, 35, 1, 36, 1, 36, 1, 37, + 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, + 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 0, 1, 68, 43, 0, 2, 4, 6, + 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, + 84, 0, 6, 1, 0, 5, 6, 1, 0, 2, 4, 2, 0, 8, 10, 45, 45, 1, 0, 41, 42, 2, 0, + 29, 29, 34, 34, 2, 0, 27, 33, 35, 36, 303, 0, 86, 1, 0, 0, 0, 2, 89, 1, 0, + 0, 0, 4, 106, 1, 0, 0, 0, 6, 108, 1, 0, 0, 0, 8, 112, 1, 0, 0, 0, 10, 121, + 1, 0, 0, 0, 12, 123, 1, 0, 0, 0, 14, 126, 1, 0, 0, 0, 16, 128, 1, 0, 0, 0, + 18, 135, 1, 0, 0, 0, 20, 139, 1, 0, 0, 0, 22, 141, 1, 0, 0, 0, 24, 151, 1, + 0, 0, 0, 26, 153, 1, 0, 0, 0, 28, 156, 1, 0, 0, 0, 30, 158, 1, 0, 0, 0, 32, + 168, 1, 0, 0, 0, 34, 170, 1, 0, 0, 0, 36, 182, 1, 0, 0, 0, 38, 187, 1, 0, 0, + 0, 40, 195, 1, 0, 0, 0, 42, 197, 1, 0, 0, 0, 44, 206, 1, 0, 0, 0, 46, 208, + 1, 0, 0, 0, 48, 212, 1, 0, 0, 0, 50, 220, 1, 0, 0, 0, 52, 222, 1, 0, 0, 0, + 54, 227, 1, 0, 0, 0, 56, 235, 1, 0, 0, 0, 58, 237, 1, 0, 0, 0, 60, 241, 1, + 0, 0, 0, 62, 251, 1, 0, 0, 0, 64, 255, 1, 0, 0, 0, 66, 269, 1, 0, 0, 0, 68, + 278, 1, 0, 0, 0, 70, 294, 1, 0, 0, 0, 72, 296, 1, 0, 0, 0, 74, 298, 1, 0, 0, + 0, 76, 303, 1, 0, 0, 0, 78, 306, 1, 0, 0, 0, 80, 310, 1, 0, 0, 0, 82, 312, + 1, 0, 0, 0, 84, 314, 1, 0, 0, 0, 86, 87, 3, 2, 1, 0, 87, 88, 5, 0, 0, 1, 88, + 1, 1, 0, 0, 0, 89, 90, 5, 1, 0, 0, 90, 91, 3, 4, 2, 0, 91, 95, 5, 18, 0, 0, + 92, 94, 3, 6, 3, 0, 93, 92, 1, 0, 0, 0, 94, 97, 1, 0, 0, 0, 95, 93, 1, 0, 0, + 0, 95, 96, 1, 0, 0, 0, 96, 101, 1, 0, 0, 0, 97, 95, 1, 0, 0, 0, 98, 100, 3, + 12, 6, 0, 99, 98, 1, 0, 0, 0, 100, 103, 1, 0, 0, 0, 101, 99, 1, 0, 0, 0, + 101, 102, 1, 0, 0, 0, 102, 104, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 104, 105, + 3, 32, 16, 0, 105, 3, 1, 0, 0, 0, 106, 107, 5, 45, 0, 0, 107, 5, 1, 0, 0, 0, + 108, 109, 7, 0, 0, 0, 109, 110, 3, 8, 4, 0, 110, 111, 5, 26, 0, 0, 111, 7, + 1, 0, 0, 0, 112, 113, 3, 22, 11, 0, 113, 118, 3, 10, 5, 0, 114, 115, 5, 25, + 0, 0, 115, 117, 3, 10, 5, 0, 116, 114, 1, 0, 0, 0, 117, 120, 1, 0, 0, 0, + 118, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 9, 1, 0, 0, 0, 120, 118, 1, + 0, 0, 0, 121, 122, 5, 45, 0, 0, 122, 11, 1, 0, 0, 0, 123, 124, 3, 14, 7, 0, + 124, 125, 3, 16, 8, 0, 125, 13, 1, 0, 0, 0, 126, 127, 7, 1, 0, 0, 127, 15, + 1, 0, 0, 0, 128, 129, 3, 20, 10, 0, 129, 130, 3, 18, 9, 0, 130, 131, 5, 20, + 0, 0, 131, 132, 3, 24, 12, 0, 132, 133, 5, 21, 0, 0, 133, 134, 3, 30, 15, 0, + 134, 17, 1, 0, 0, 0, 135, 136, 5, 45, 0, 0, 136, 19, 1, 0, 0, 0, 137, 140, + 3, 22, 11, 0, 138, 140, 5, 11, 0, 0, 139, 137, 1, 0, 0, 0, 139, 138, 1, 0, + 0, 0, 140, 21, 1, 0, 0, 0, 141, 142, 7, 2, 0, 0, 142, 23, 1, 0, 0, 0, 143, + 148, 3, 26, 13, 0, 144, 145, 5, 25, 0, 0, 145, 147, 3, 26, 13, 0, 146, 144, + 1, 0, 0, 0, 147, 150, 1, 0, 0, 0, 148, 146, 1, 0, 0, 0, 148, 149, 1, 0, 0, + 0, 149, 152, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 151, 143, 1, 0, 0, 0, 151, + 152, 1, 0, 0, 0, 152, 25, 1, 0, 0, 0, 153, 154, 3, 22, 11, 0, 154, 155, 3, + 28, 14, 0, 155, 27, 1, 0, 0, 0, 156, 157, 5, 45, 0, 0, 157, 29, 1, 0, 0, 0, + 158, 162, 5, 18, 0, 0, 159, 161, 3, 34, 17, 0, 160, 159, 1, 0, 0, 0, 161, + 164, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 162, 163, 1, 0, 0, 0, 163, 165, 1, 0, + 0, 0, 164, 162, 1, 0, 0, 0, 165, 166, 3, 38, 19, 0, 166, 167, 3, 32, 16, 0, + 167, 31, 1, 0, 0, 0, 168, 169, 5, 19, 0, 0, 169, 33, 1, 0, 0, 0, 170, 171, + 5, 7, 0, 0, 171, 172, 3, 22, 11, 0, 172, 177, 3, 36, 18, 0, 173, 174, 5, 25, + 0, 0, 174, 176, 3, 36, 18, 0, 175, 173, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, + 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, + 1, 0, 0, 0, 180, 181, 5, 26, 0, 0, 181, 35, 1, 0, 0, 0, 182, 183, 5, 45, 0, + 0, 183, 37, 1, 0, 0, 0, 184, 186, 3, 40, 20, 0, 185, 184, 1, 0, 0, 0, 186, + 189, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 39, 1, 0, + 0, 0, 189, 187, 1, 0, 0, 0, 190, 196, 3, 42, 21, 0, 191, 196, 3, 46, 23, 0, + 192, 196, 3, 54, 27, 0, 193, 196, 3, 58, 29, 0, 194, 196, 3, 64, 32, 0, 195, + 190, 1, 0, 0, 0, 195, 191, 1, 0, 0, 0, 195, 192, 1, 0, 0, 0, 195, 193, 1, 0, + 0, 0, 195, 194, 1, 0, 0, 0, 196, 41, 1, 0, 0, 0, 197, 200, 5, 12, 0, 0, 198, + 201, 3, 72, 36, 0, 199, 201, 3, 74, 37, 0, 200, 198, 1, 0, 0, 0, 200, 199, + 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 3, 44, 22, 0, 203, 204, 3, 68, + 34, 0, 204, 205, 5, 26, 0, 0, 205, 43, 1, 0, 0, 0, 206, 207, 5, 27, 0, 0, + 207, 45, 1, 0, 0, 0, 208, 210, 3, 48, 24, 0, 209, 211, 3, 52, 26, 0, 210, + 209, 1, 0, 0, 0, 210, 211, 1, 0, 0, 0, 211, 47, 1, 0, 0, 0, 212, 213, 5, 14, + 0, 0, 213, 214, 5, 20, 0, 0, 214, 215, 3, 50, 25, 0, 215, 216, 5, 21, 0, 0, + 216, 217, 5, 18, 0, 0, 217, 218, 3, 38, 19, 0, 218, 219, 3, 32, 16, 0, 219, + 49, 1, 0, 0, 0, 220, 221, 3, 68, 34, 0, 221, 51, 1, 0, 0, 0, 222, 223, 5, + 15, 0, 0, 223, 224, 5, 18, 0, 0, 224, 225, 3, 38, 19, 0, 225, 226, 3, 32, + 16, 0, 226, 53, 1, 0, 0, 0, 227, 228, 5, 16, 0, 0, 228, 229, 5, 20, 0, 0, + 229, 230, 3, 56, 28, 0, 230, 231, 5, 21, 0, 0, 231, 232, 5, 18, 0, 0, 232, + 233, 3, 38, 19, 0, 233, 234, 3, 32, 16, 0, 234, 55, 1, 0, 0, 0, 235, 236, 3, + 68, 34, 0, 236, 57, 1, 0, 0, 0, 237, 238, 5, 13, 0, 0, 238, 239, 3, 60, 30, + 0, 239, 240, 5, 26, 0, 0, 240, 59, 1, 0, 0, 0, 241, 242, 3, 62, 31, 0, 242, + 243, 5, 20, 0, 0, 243, 244, 3, 66, 33, 0, 244, 245, 5, 21, 0, 0, 245, 61, 1, + 0, 0, 0, 246, 249, 3, 4, 2, 0, 247, 249, 5, 44, 0, 0, 248, 246, 1, 0, 0, 0, + 248, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 252, 5, 24, 0, 0, 251, 248, + 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 254, 3, 18, 9, + 0, 254, 63, 1, 0, 0, 0, 255, 257, 5, 17, 0, 0, 256, 258, 3, 68, 34, 0, 257, + 256, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 260, 5, + 26, 0, 0, 260, 65, 1, 0, 0, 0, 261, 266, 3, 68, 34, 0, 262, 263, 5, 25, 0, + 0, 263, 265, 3, 68, 34, 0, 264, 262, 1, 0, 0, 0, 265, 268, 1, 0, 0, 0, 266, + 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 270, 1, 0, 0, 0, 268, 266, 1, 0, + 0, 0, 269, 261, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 67, 1, 0, 0, 0, 271, + 272, 6, 34, -1, 0, 272, 279, 3, 70, 35, 0, 273, 279, 3, 72, 36, 0, 274, 279, + 3, 60, 30, 0, 275, 279, 3, 74, 37, 0, 276, 279, 3, 76, 38, 0, 277, 279, 3, + 78, 39, 0, 278, 271, 1, 0, 0, 0, 278, 273, 1, 0, 0, 0, 278, 274, 1, 0, 0, 0, + 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 278, 277, 1, 0, 0, 0, 279, 286, + 1, 0, 0, 0, 280, 281, 10, 2, 0, 0, 281, 282, 3, 84, 42, 0, 282, 283, 3, 68, + 34, 3, 283, 285, 1, 0, 0, 0, 284, 280, 1, 0, 0, 0, 285, 288, 1, 0, 0, 0, + 286, 284, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 69, 1, 0, 0, 0, 288, 286, + 1, 0, 0, 0, 289, 295, 5, 40, 0, 0, 290, 295, 5, 46, 0, 0, 291, 295, 3, 80, + 40, 0, 292, 295, 5, 43, 0, 0, 293, 295, 5, 44, 0, 0, 294, 289, 1, 0, 0, 0, + 294, 290, 1, 0, 0, 0, 294, 291, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 294, 293, + 1, 0, 0, 0, 295, 71, 1, 0, 0, 0, 296, 297, 5, 45, 0, 0, 297, 73, 1, 0, 0, 0, + 298, 299, 3, 72, 36, 0, 299, 300, 5, 22, 0, 0, 300, 301, 3, 68, 34, 0, 301, + 302, 5, 23, 0, 0, 302, 75, 1, 0, 0, 0, 303, 304, 3, 82, 41, 0, 304, 305, 3, + 68, 34, 0, 305, 77, 1, 0, 0, 0, 306, 307, 5, 20, 0, 0, 307, 308, 3, 68, 34, + 0, 308, 309, 5, 21, 0, 0, 309, 79, 1, 0, 0, 0, 310, 311, 7, 3, 0, 0, 311, + 81, 1, 0, 0, 0, 312, 313, 7, 4, 0, 0, 313, 83, 1, 0, 0, 0, 314, 315, 7, 5, + 0, 0, 315, 85, 1, 0, 0, 0, 20, 95, 101, 118, 139, 148, 151, 162, 177, 187, + 195, 200, 210, 248, 251, 257, 266, 269, 278, 286, 294, + ]; + + private static __ATN: antlr.ATN; + public static get _ATN(): antlr.ATN { + if (!JackParser.__ATN) { + JackParser.__ATN = new antlr.ATNDeserializer().deserialize( + JackParser._serializedATN, + ); + } + + return JackParser.__ATN; + } + + private static readonly vocabulary = new antlr.Vocabulary( + JackParser.literalNames, + JackParser.symbolicNames, + [], + ); + + public override get vocabulary(): antlr.Vocabulary { + return JackParser.vocabulary; + } + + private static readonly decisionsToDFA = JackParser._ATN.decisionToState.map( + (ds: antlr.DecisionState, index: number) => new antlr.DFA(ds, index), + ); +} + +export class ProgramContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public classDeclaration(): ClassDeclarationContext { + return assertExists(this.getRuleContext(0, ClassDeclarationContext)); + } + public EOF(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.EOF, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_program; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterProgram) { + listener.enterProgram(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitProgram) { + listener.exitProgram(this); + } + } +} + +export class ClassDeclarationContext extends antlr.ParserRuleContext { + public localSymbolTable: LocalSymbolTable | undefined; + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public CLASS(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.CLASS, 0)); + } + public className(): ClassNameContext { + return assertExists(this.getRuleContext(0, ClassNameContext)); + } + public LBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACE, 0)); + } + public rBrace(): RBraceContext { + return assertExists(this.getRuleContext(0, RBraceContext)); + } + public classVarDec(): ClassVarDecContext[]; + public classVarDec(i: number): ClassVarDecContext | null; + public classVarDec( + i?: number, + ): ClassVarDecContext[] | ClassVarDecContext | null { + if (i === undefined) { + return this.getRuleContexts(ClassVarDecContext); + } + + return this.getRuleContext(i, ClassVarDecContext); + } + public subroutineDeclaration(): SubroutineDeclarationContext[]; + public subroutineDeclaration(i: number): SubroutineDeclarationContext | null; + public subroutineDeclaration( + i?: number, + ): SubroutineDeclarationContext[] | SubroutineDeclarationContext | null { + if (i === undefined) { + return this.getRuleContexts(SubroutineDeclarationContext); + } + + return this.getRuleContext(i, SubroutineDeclarationContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_classDeclaration; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterClassDeclaration) { + listener.enterClassDeclaration(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitClassDeclaration) { + listener.exitClassDeclaration(this); + } + } +} + +export class ClassNameContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_className; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterClassName) { + listener.enterClassName(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitClassName) { + listener.exitClassName(this); + } + } +} + +export class ClassVarDecContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public fieldList(): FieldListContext { + return assertExists(this.getRuleContext(0, FieldListContext)); + } + public SEMICOLON(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.SEMICOLON, 0)); + } + public STATIC(): antlr.TerminalNode | null { + return this.getToken(JackParser.STATIC, 0); + } + public FIELD(): antlr.TerminalNode | null { + return this.getToken(JackParser.FIELD, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_classVarDec; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterClassVarDec) { + listener.enterClassVarDec(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitClassVarDec) { + listener.exitClassVarDec(this); + } + } +} + +export class FieldListContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public varType(): VarTypeContext { + return assertExists(this.getRuleContext(0, VarTypeContext)); + } + public fieldName(): FieldNameContext[]; + public fieldName(i: number): FieldNameContext | null; + public fieldName(i?: number): FieldNameContext[] | FieldNameContext | null { + if (i === undefined) { + return this.getRuleContexts(FieldNameContext); + } + + return this.getRuleContext(i, FieldNameContext); + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(JackParser.COMMA); + } else { + return this.getToken(JackParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return JackParser.RULE_fieldList; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterFieldList) { + listener.enterFieldList(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitFieldList) { + listener.exitFieldList(this); + } + } +} + +export class FieldNameContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_fieldName; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterFieldName) { + listener.enterFieldName(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitFieldName) { + listener.exitFieldName(this); + } + } +} + +export class SubroutineDeclarationContext extends antlr.ParserRuleContext { + public symbols: SubroutineScope | undefined; + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public subroutineType(): SubroutineTypeContext { + return assertExists(this.getRuleContext(0, SubroutineTypeContext)); + } + public subroutineDecWithoutType(): SubroutineDecWithoutTypeContext { + return assertExists( + this.getRuleContext(0, SubroutineDecWithoutTypeContext), + ); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineDeclaration; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineDeclaration) { + listener.enterSubroutineDeclaration(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineDeclaration) { + listener.exitSubroutineDeclaration(this); + } + } +} + +export class SubroutineTypeContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public CONSTRUCTOR(): antlr.TerminalNode | null { + return this.getToken(JackParser.CONSTRUCTOR, 0); + } + public METHOD(): antlr.TerminalNode | null { + return this.getToken(JackParser.METHOD, 0); + } + public FUNCTION(): antlr.TerminalNode | null { + return this.getToken(JackParser.FUNCTION, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineType; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineType) { + listener.enterSubroutineType(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineType) { + listener.exitSubroutineType(this); + } + } +} + +export class SubroutineDecWithoutTypeContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public subroutineReturnType(): SubroutineReturnTypeContext { + return assertExists(this.getRuleContext(0, SubroutineReturnTypeContext)); + } + public subroutineName(): SubroutineNameContext { + return assertExists(this.getRuleContext(0, SubroutineNameContext)); + } + public LPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LPAREN, 0)); + } + public parameterList(): ParameterListContext { + return assertExists(this.getRuleContext(0, ParameterListContext)); + } + public RPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RPAREN, 0)); + } + public subroutineBody(): SubroutineBodyContext { + return assertExists(this.getRuleContext(0, SubroutineBodyContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineDecWithoutType; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineDecWithoutType) { + listener.enterSubroutineDecWithoutType(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineDecWithoutType) { + listener.exitSubroutineDecWithoutType(this); + } + } +} + +export class SubroutineNameContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineName; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineName) { + listener.enterSubroutineName(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineName) { + listener.exitSubroutineName(this); + } + } +} + +export class SubroutineReturnTypeContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public varType(): VarTypeContext | null { + return this.getRuleContext(0, VarTypeContext); + } + public VOID(): antlr.TerminalNode | null { + return this.getToken(JackParser.VOID, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineReturnType; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineReturnType) { + listener.enterSubroutineReturnType(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineReturnType) { + listener.exitSubroutineReturnType(this); + } + } +} + +export class VarTypeContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public INT(): antlr.TerminalNode | null { + return this.getToken(JackParser.INT, 0); + } + public CHAR(): antlr.TerminalNode | null { + return this.getToken(JackParser.CHAR, 0); + } + public BOOLEAN(): antlr.TerminalNode | null { + return this.getToken(JackParser.BOOLEAN, 0); + } + public IDENTIFIER(): antlr.TerminalNode | null { + return this.getToken(JackParser.IDENTIFIER, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_varType; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterVarType) { + listener.enterVarType(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitVarType) { + listener.exitVarType(this); + } + } +} + +export class ParameterListContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public parameter(): ParameterContext[]; + public parameter(i: number): ParameterContext | null; + public parameter(i?: number): ParameterContext[] | ParameterContext | null { + if (i === undefined) { + return this.getRuleContexts(ParameterContext); + } + + return this.getRuleContext(i, ParameterContext); + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(JackParser.COMMA); + } else { + return this.getToken(JackParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return JackParser.RULE_parameterList; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterParameterList) { + listener.enterParameterList(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitParameterList) { + listener.exitParameterList(this); + } + } +} + +export class ParameterContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public varType(): VarTypeContext { + return assertExists(this.getRuleContext(0, VarTypeContext)); + } + public parameterName(): ParameterNameContext { + return assertExists(this.getRuleContext(0, ParameterNameContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_parameter; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterParameter) { + listener.enterParameter(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitParameter) { + listener.exitParameter(this); + } + } +} + +export class ParameterNameContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_parameterName; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterParameterName) { + listener.enterParameterName(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitParameterName) { + listener.exitParameterName(this); + } + } +} + +export class SubroutineBodyContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public LBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACE, 0)); + } + public statements(): StatementsContext { + return assertExists(this.getRuleContext(0, StatementsContext)); + } + public rBrace(): RBraceContext { + return assertExists(this.getRuleContext(0, RBraceContext)); + } + public varDeclaration(): VarDeclarationContext[]; + public varDeclaration(i: number): VarDeclarationContext | null; + public varDeclaration( + i?: number, + ): VarDeclarationContext[] | VarDeclarationContext | null { + if (i === undefined) { + return this.getRuleContexts(VarDeclarationContext); + } + + return this.getRuleContext(i, VarDeclarationContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineBody; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineBody) { + listener.enterSubroutineBody(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineBody) { + listener.exitSubroutineBody(this); + } + } +} + +export class RBraceContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public RBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RBRACE, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_rBrace; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterRBrace) { + listener.enterRBrace(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitRBrace) { + listener.exitRBrace(this); + } + } +} + +export class VarDeclarationContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public VAR(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.VAR, 0)); + } + public varType(): VarTypeContext { + return assertExists(this.getRuleContext(0, VarTypeContext)); + } + public varNameInDeclaration(): VarNameInDeclarationContext[]; + public varNameInDeclaration(i: number): VarNameInDeclarationContext | null; + public varNameInDeclaration( + i?: number, + ): VarNameInDeclarationContext[] | VarNameInDeclarationContext | null { + if (i === undefined) { + return this.getRuleContexts(VarNameInDeclarationContext); + } + + return this.getRuleContext(i, VarNameInDeclarationContext); + } + public SEMICOLON(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.SEMICOLON, 0)); + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(JackParser.COMMA); + } else { + return this.getToken(JackParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return JackParser.RULE_varDeclaration; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterVarDeclaration) { + listener.enterVarDeclaration(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitVarDeclaration) { + listener.exitVarDeclaration(this); + } + } +} + +export class VarNameInDeclarationContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_varNameInDeclaration; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterVarNameInDeclaration) { + listener.enterVarNameInDeclaration(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitVarNameInDeclaration) { + listener.exitVarNameInDeclaration(this); + } + } +} + +export class StatementsContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public statement(): StatementContext[]; + public statement(i: number): StatementContext | null; + public statement(i?: number): StatementContext[] | StatementContext | null { + if (i === undefined) { + return this.getRuleContexts(StatementContext); + } + + return this.getRuleContext(i, StatementContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_statements; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterStatements) { + listener.enterStatements(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitStatements) { + listener.exitStatements(this); + } + } +} + +export class StatementContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public letStatement(): LetStatementContext | null { + return this.getRuleContext(0, LetStatementContext); + } + public ifElseStatement(): IfElseStatementContext | null { + return this.getRuleContext(0, IfElseStatementContext); + } + public whileStatement(): WhileStatementContext | null { + return this.getRuleContext(0, WhileStatementContext); + } + public doStatement(): DoStatementContext | null { + return this.getRuleContext(0, DoStatementContext); + } + public returnStatement(): ReturnStatementContext | null { + return this.getRuleContext(0, ReturnStatementContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_statement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterStatement) { + listener.enterStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitStatement) { + listener.exitStatement(this); + } + } +} + +export class LetStatementContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public LET(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LET, 0)); + } + public equals(): EqualsContext { + return assertExists(this.getRuleContext(0, EqualsContext)); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public SEMICOLON(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.SEMICOLON, 0)); + } + public varName(): VarNameContext | null { + return this.getRuleContext(0, VarNameContext); + } + public arrayAccess(): ArrayAccessContext | null { + return this.getRuleContext(0, ArrayAccessContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_letStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterLetStatement) { + listener.enterLetStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitLetStatement) { + listener.exitLetStatement(this); + } + } +} + +export class EqualsContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public EQUALS(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.EQUALS, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_equals; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterEquals) { + listener.enterEquals(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitEquals) { + listener.exitEquals(this); + } + } +} + +export class IfElseStatementContext extends antlr.ParserRuleContext { + public endLabel = ""; + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public ifStatement(): IfStatementContext { + return assertExists(this.getRuleContext(0, IfStatementContext)); + } + public elseStatement(): ElseStatementContext | null { + return this.getRuleContext(0, ElseStatementContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_ifElseStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterIfElseStatement) { + listener.enterIfElseStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitIfElseStatement) { + listener.exitIfElseStatement(this); + } + } +} + +export class IfStatementContext extends antlr.ParserRuleContext { + public endLabel = ""; + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IF(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IF, 0)); + } + public LPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LPAREN, 0)); + } + public ifExpression(): IfExpressionContext { + return assertExists(this.getRuleContext(0, IfExpressionContext)); + } + public RPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RPAREN, 0)); + } + public LBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACE, 0)); + } + public statements(): StatementsContext { + return assertExists(this.getRuleContext(0, StatementsContext)); + } + public rBrace(): RBraceContext { + return assertExists(this.getRuleContext(0, RBraceContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_ifStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterIfStatement) { + listener.enterIfStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitIfStatement) { + listener.exitIfStatement(this); + } + } +} + +export class IfExpressionContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_ifExpression; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterIfExpression) { + listener.enterIfExpression(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitIfExpression) { + listener.exitIfExpression(this); + } + } +} + +export class ElseStatementContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public ELSE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.ELSE, 0)); + } + public LBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACE, 0)); + } + public statements(): StatementsContext { + return assertExists(this.getRuleContext(0, StatementsContext)); + } + public rBrace(): RBraceContext { + return assertExists(this.getRuleContext(0, RBraceContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_elseStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterElseStatement) { + listener.enterElseStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitElseStatement) { + listener.exitElseStatement(this); + } + } +} + +export class WhileStatementContext extends antlr.ParserRuleContext { + public startLabel = ""; + endLabel = ""; + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public WHILE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.WHILE, 0)); + } + public LPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LPAREN, 0)); + } + public whileExpression(): WhileExpressionContext { + return assertExists(this.getRuleContext(0, WhileExpressionContext)); + } + public RPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RPAREN, 0)); + } + public LBRACE(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACE, 0)); + } + public statements(): StatementsContext { + return assertExists(this.getRuleContext(0, StatementsContext)); + } + public rBrace(): RBraceContext { + return assertExists(this.getRuleContext(0, RBraceContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_whileStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterWhileStatement) { + listener.enterWhileStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitWhileStatement) { + listener.exitWhileStatement(this); + } + } +} + +export class WhileExpressionContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_whileExpression; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterWhileExpression) { + listener.enterWhileExpression(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitWhileExpression) { + listener.exitWhileExpression(this); + } + } +} + +export class DoStatementContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public DO(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.DO, 0)); + } + public subroutineCall(): SubroutineCallContext { + return assertExists(this.getRuleContext(0, SubroutineCallContext)); + } + public SEMICOLON(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.SEMICOLON, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_doStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterDoStatement) { + listener.enterDoStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitDoStatement) { + listener.exitDoStatement(this); + } + } +} + +export class SubroutineCallContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public subroutineId(): SubroutineIdContext { + return assertExists(this.getRuleContext(0, SubroutineIdContext)); + } + public LPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LPAREN, 0)); + } + public expressionList(): ExpressionListContext { + return assertExists(this.getRuleContext(0, ExpressionListContext)); + } + public RPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RPAREN, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineCall; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineCall) { + listener.enterSubroutineCall(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineCall) { + listener.exitSubroutineCall(this); + } + } +} + +export class SubroutineIdContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public subroutineName(): SubroutineNameContext { + return assertExists(this.getRuleContext(0, SubroutineNameContext)); + } + public DOT(): antlr.TerminalNode | null { + return this.getToken(JackParser.DOT, 0); + } + public className(): ClassNameContext | null { + return this.getRuleContext(0, ClassNameContext); + } + public THIS_LITERAL(): antlr.TerminalNode | null { + return this.getToken(JackParser.THIS_LITERAL, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_subroutineId; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterSubroutineId) { + listener.enterSubroutineId(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitSubroutineId) { + listener.exitSubroutineId(this); + } + } +} + +export class ReturnStatementContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public RETURN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RETURN, 0)); + } + public SEMICOLON(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.SEMICOLON, 0)); + } + public expression(): ExpressionContext | null { + return this.getRuleContext(0, ExpressionContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_returnStatement; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterReturnStatement) { + listener.enterReturnStatement(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitReturnStatement) { + listener.exitReturnStatement(this); + } + } +} + +export class ExpressionListContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public expression(): ExpressionContext[]; + public expression(i: number): ExpressionContext | null; + public expression( + i?: number, + ): ExpressionContext[] | ExpressionContext | null { + if (i === undefined) { + return this.getRuleContexts(ExpressionContext); + } + + return this.getRuleContext(i, ExpressionContext); + } + public COMMA(): antlr.TerminalNode[]; + public COMMA(i: number): antlr.TerminalNode | null; + public COMMA(i?: number): antlr.TerminalNode | null | antlr.TerminalNode[] { + if (i === undefined) { + return this.getTokens(JackParser.COMMA); + } else { + return this.getToken(JackParser.COMMA, i); + } + } + public override get ruleIndex(): number { + return JackParser.RULE_expressionList; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterExpressionList) { + listener.enterExpressionList(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitExpressionList) { + listener.exitExpressionList(this); + } + } +} + +export class ExpressionContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public constant(): ConstantContext | null { + return this.getRuleContext(0, ConstantContext); + } + public varName(): VarNameContext | null { + return this.getRuleContext(0, VarNameContext); + } + public subroutineCall(): SubroutineCallContext | null { + return this.getRuleContext(0, SubroutineCallContext); + } + public arrayAccess(): ArrayAccessContext | null { + return this.getRuleContext(0, ArrayAccessContext); + } + public unaryOperation(): UnaryOperationContext | null { + return this.getRuleContext(0, UnaryOperationContext); + } + public groupedExpression(): GroupedExpressionContext | null { + return this.getRuleContext(0, GroupedExpressionContext); + } + public expression(): ExpressionContext[]; + public expression(i: number): ExpressionContext | null; + public expression( + i?: number, + ): ExpressionContext[] | ExpressionContext | null { + if (i === undefined) { + return this.getRuleContexts(ExpressionContext); + } + + return this.getRuleContext(i, ExpressionContext); + } + public binaryOperator(): BinaryOperatorContext | null { + return this.getRuleContext(0, BinaryOperatorContext); + } + public override get ruleIndex(): number { + return JackParser.RULE_expression; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterExpression) { + listener.enterExpression(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitExpression) { + listener.exitExpression(this); + } + } +} + +export class ConstantContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public INTEGER_LITERAL(): antlr.TerminalNode | null { + return this.getToken(JackParser.INTEGER_LITERAL, 0); + } + public STRING_LITERAL(): antlr.TerminalNode | null { + return this.getToken(JackParser.STRING_LITERAL, 0); + } + public booleanLiteral(): BooleanLiteralContext | null { + return this.getRuleContext(0, BooleanLiteralContext); + } + public NULL_LITERAL(): antlr.TerminalNode | null { + return this.getToken(JackParser.NULL_LITERAL, 0); + } + public THIS_LITERAL(): antlr.TerminalNode | null { + return this.getToken(JackParser.THIS_LITERAL, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_constant; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterConstant) { + listener.enterConstant(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitConstant) { + listener.exitConstant(this); + } + } +} + +export class VarNameContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public IDENTIFIER(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.IDENTIFIER, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_varName; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterVarName) { + listener.enterVarName(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitVarName) { + listener.exitVarName(this); + } + } +} + +export class ArrayAccessContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public varName(): VarNameContext { + return assertExists(this.getRuleContext(0, VarNameContext)); + } + public LBRACKET(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LBRACKET, 0)); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public RBRACKET(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RBRACKET, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_arrayAccess; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterArrayAccess) { + listener.enterArrayAccess(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitArrayAccess) { + listener.exitArrayAccess(this); + } + } +} + +export class UnaryOperationContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public unaryOperator(): UnaryOperatorContext { + return assertExists(this.getRuleContext(0, UnaryOperatorContext)); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public override get ruleIndex(): number { + return JackParser.RULE_unaryOperation; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterUnaryOperation) { + listener.enterUnaryOperation(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitUnaryOperation) { + listener.exitUnaryOperation(this); + } + } +} + +export class GroupedExpressionContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public LPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.LPAREN, 0)); + } + public expression(): ExpressionContext { + return assertExists(this.getRuleContext(0, ExpressionContext)); + } + public RPAREN(): antlr.TerminalNode { + return assertExists(this.getToken(JackParser.RPAREN, 0)); + } + public override get ruleIndex(): number { + return JackParser.RULE_groupedExpression; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterGroupedExpression) { + listener.enterGroupedExpression(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitGroupedExpression) { + listener.exitGroupedExpression(this); + } + } +} + +export class BooleanLiteralContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public TRUE(): antlr.TerminalNode | null { + return this.getToken(JackParser.TRUE, 0); + } + public FALSE(): antlr.TerminalNode | null { + return this.getToken(JackParser.FALSE, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_booleanLiteral; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterBooleanLiteral) { + listener.enterBooleanLiteral(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitBooleanLiteral) { + listener.exitBooleanLiteral(this); + } + } +} + +export class UnaryOperatorContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public TILDE(): antlr.TerminalNode | null { + return this.getToken(JackParser.TILDE, 0); + } + public MINUS(): antlr.TerminalNode | null { + return this.getToken(JackParser.MINUS, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_unaryOperator; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterUnaryOperator) { + listener.enterUnaryOperator(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitUnaryOperator) { + listener.exitUnaryOperator(this); + } + } +} + +export class BinaryOperatorContext extends antlr.ParserRuleContext { + public constructor( + parent: antlr.ParserRuleContext | null, + invokingState: number, + ) { + super(parent, invokingState); + } + public PLUS(): antlr.TerminalNode | null { + return this.getToken(JackParser.PLUS, 0); + } + public MINUS(): antlr.TerminalNode | null { + return this.getToken(JackParser.MINUS, 0); + } + public MUL(): antlr.TerminalNode | null { + return this.getToken(JackParser.MUL, 0); + } + public DIV(): antlr.TerminalNode | null { + return this.getToken(JackParser.DIV, 0); + } + public AND(): antlr.TerminalNode | null { + return this.getToken(JackParser.AND, 0); + } + public OR(): antlr.TerminalNode | null { + return this.getToken(JackParser.OR, 0); + } + public LESS_THAN(): antlr.TerminalNode | null { + return this.getToken(JackParser.LESS_THAN, 0); + } + public GREATER_THAN(): antlr.TerminalNode | null { + return this.getToken(JackParser.GREATER_THAN, 0); + } + public EQUALS(): antlr.TerminalNode | null { + return this.getToken(JackParser.EQUALS, 0); + } + public override get ruleIndex(): number { + return JackParser.RULE_binaryOperator; + } + public override enterRule(listener: JackParserListener): void { + if (listener.enterBinaryOperator) { + listener.enterBinaryOperator(this); + } + } + public override exitRule(listener: JackParserListener): void { + if (listener.exitBinaryOperator) { + listener.exitBinaryOperator(this); + } + } +} diff --git a/simulator/src/jack/generated/JackParserListener.ts b/simulator/src/jack/generated/JackParserListener.ts new file mode 100644 index 000000000..b6875bd38 --- /dev/null +++ b/simulator/src/jack/generated/JackParserListener.ts @@ -0,0 +1,506 @@ +// Generated from JackParser.g4 by ANTLR 4.13.1 + +import { + ErrorNode, + ParserRuleContext, + ParseTreeListener, + TerminalNode, +} from "antlr4ng"; + +import { + ArrayAccessContext, + BinaryOperatorContext, + BooleanLiteralContext, + ClassDeclarationContext, + ClassNameContext, + ClassVarDecContext, + ConstantContext, + DoStatementContext, + ElseStatementContext, + EqualsContext, + ExpressionContext, + ExpressionListContext, + FieldListContext, + FieldNameContext, + GroupedExpressionContext, + IfElseStatementContext, + IfExpressionContext, + IfStatementContext, + LetStatementContext, + ParameterContext, + ParameterListContext, + ParameterNameContext, + ProgramContext, + RBraceContext, + ReturnStatementContext, + StatementContext, + StatementsContext, + SubroutineBodyContext, + SubroutineCallContext, + SubroutineDecWithoutTypeContext, + SubroutineDeclarationContext, + SubroutineIdContext, + SubroutineNameContext, + SubroutineReturnTypeContext, + SubroutineTypeContext, + UnaryOperationContext, + UnaryOperatorContext, + VarDeclarationContext, + VarNameContext, + VarNameInDeclarationContext, + VarTypeContext, + WhileExpressionContext, + WhileStatementContext, +} from "./JackParser.js"; + +/** + * This interface defines a complete listener for a parse tree produced by + * `JackParser`. + */ +export class JackParserListener implements ParseTreeListener { + /** + * Enter a parse tree produced by `JackParser.program`. + * @param ctx the parse tree + */ + enterProgram?: (ctx: ProgramContext) => void; + /** + * Exit a parse tree produced by `JackParser.program`. + * @param ctx the parse tree + */ + exitProgram?: (ctx: ProgramContext) => void; + /** + * Enter a parse tree produced by `JackParser.classDeclaration`. + * @param ctx the parse tree + */ + enterClassDeclaration?: (ctx: ClassDeclarationContext) => void; + /** + * Exit a parse tree produced by `JackParser.classDeclaration`. + * @param ctx the parse tree + */ + exitClassDeclaration?: (ctx: ClassDeclarationContext) => void; + /** + * Enter a parse tree produced by `JackParser.className`. + * @param ctx the parse tree + */ + enterClassName?: (ctx: ClassNameContext) => void; + /** + * Exit a parse tree produced by `JackParser.className`. + * @param ctx the parse tree + */ + exitClassName?: (ctx: ClassNameContext) => void; + /** + * Enter a parse tree produced by `JackParser.classVarDec`. + * @param ctx the parse tree + */ + enterClassVarDec?: (ctx: ClassVarDecContext) => void; + /** + * Exit a parse tree produced by `JackParser.classVarDec`. + * @param ctx the parse tree + */ + exitClassVarDec?: (ctx: ClassVarDecContext) => void; + /** + * Enter a parse tree produced by `JackParser.fieldList`. + * @param ctx the parse tree + */ + enterFieldList?: (ctx: FieldListContext) => void; + /** + * Exit a parse tree produced by `JackParser.fieldList`. + * @param ctx the parse tree + */ + exitFieldList?: (ctx: FieldListContext) => void; + /** + * Enter a parse tree produced by `JackParser.fieldName`. + * @param ctx the parse tree + */ + enterFieldName?: (ctx: FieldNameContext) => void; + /** + * Exit a parse tree produced by `JackParser.fieldName`. + * @param ctx the parse tree + */ + exitFieldName?: (ctx: FieldNameContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineDeclaration`. + * @param ctx the parse tree + */ + enterSubroutineDeclaration?: (ctx: SubroutineDeclarationContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineDeclaration`. + * @param ctx the parse tree + */ + exitSubroutineDeclaration?: (ctx: SubroutineDeclarationContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineType`. + * @param ctx the parse tree + */ + enterSubroutineType?: (ctx: SubroutineTypeContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineType`. + * @param ctx the parse tree + */ + exitSubroutineType?: (ctx: SubroutineTypeContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineDecWithoutType`. + * @param ctx the parse tree + */ + enterSubroutineDecWithoutType?: ( + ctx: SubroutineDecWithoutTypeContext, + ) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineDecWithoutType`. + * @param ctx the parse tree + */ + exitSubroutineDecWithoutType?: (ctx: SubroutineDecWithoutTypeContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineName`. + * @param ctx the parse tree + */ + enterSubroutineName?: (ctx: SubroutineNameContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineName`. + * @param ctx the parse tree + */ + exitSubroutineName?: (ctx: SubroutineNameContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineReturnType`. + * @param ctx the parse tree + */ + enterSubroutineReturnType?: (ctx: SubroutineReturnTypeContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineReturnType`. + * @param ctx the parse tree + */ + exitSubroutineReturnType?: (ctx: SubroutineReturnTypeContext) => void; + /** + * Enter a parse tree produced by `JackParser.varType`. + * @param ctx the parse tree + */ + enterVarType?: (ctx: VarTypeContext) => void; + /** + * Exit a parse tree produced by `JackParser.varType`. + * @param ctx the parse tree + */ + exitVarType?: (ctx: VarTypeContext) => void; + /** + * Enter a parse tree produced by `JackParser.parameterList`. + * @param ctx the parse tree + */ + enterParameterList?: (ctx: ParameterListContext) => void; + /** + * Exit a parse tree produced by `JackParser.parameterList`. + * @param ctx the parse tree + */ + exitParameterList?: (ctx: ParameterListContext) => void; + /** + * Enter a parse tree produced by `JackParser.parameter`. + * @param ctx the parse tree + */ + enterParameter?: (ctx: ParameterContext) => void; + /** + * Exit a parse tree produced by `JackParser.parameter`. + * @param ctx the parse tree + */ + exitParameter?: (ctx: ParameterContext) => void; + /** + * Enter a parse tree produced by `JackParser.parameterName`. + * @param ctx the parse tree + */ + enterParameterName?: (ctx: ParameterNameContext) => void; + /** + * Exit a parse tree produced by `JackParser.parameterName`. + * @param ctx the parse tree + */ + exitParameterName?: (ctx: ParameterNameContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineBody`. + * @param ctx the parse tree + */ + enterSubroutineBody?: (ctx: SubroutineBodyContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineBody`. + * @param ctx the parse tree + */ + exitSubroutineBody?: (ctx: SubroutineBodyContext) => void; + /** + * Enter a parse tree produced by `JackParser.rBrace`. + * @param ctx the parse tree + */ + enterRBrace?: (ctx: RBraceContext) => void; + /** + * Exit a parse tree produced by `JackParser.rBrace`. + * @param ctx the parse tree + */ + exitRBrace?: (ctx: RBraceContext) => void; + /** + * Enter a parse tree produced by `JackParser.varDeclaration`. + * @param ctx the parse tree + */ + enterVarDeclaration?: (ctx: VarDeclarationContext) => void; + /** + * Exit a parse tree produced by `JackParser.varDeclaration`. + * @param ctx the parse tree + */ + exitVarDeclaration?: (ctx: VarDeclarationContext) => void; + /** + * Enter a parse tree produced by `JackParser.varNameInDeclaration`. + * @param ctx the parse tree + */ + enterVarNameInDeclaration?: (ctx: VarNameInDeclarationContext) => void; + /** + * Exit a parse tree produced by `JackParser.varNameInDeclaration`. + * @param ctx the parse tree + */ + exitVarNameInDeclaration?: (ctx: VarNameInDeclarationContext) => void; + /** + * Enter a parse tree produced by `JackParser.statements`. + * @param ctx the parse tree + */ + enterStatements?: (ctx: StatementsContext) => void; + /** + * Exit a parse tree produced by `JackParser.statements`. + * @param ctx the parse tree + */ + exitStatements?: (ctx: StatementsContext) => void; + /** + * Enter a parse tree produced by `JackParser.statement`. + * @param ctx the parse tree + */ + enterStatement?: (ctx: StatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.statement`. + * @param ctx the parse tree + */ + exitStatement?: (ctx: StatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.letStatement`. + * @param ctx the parse tree + */ + enterLetStatement?: (ctx: LetStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.letStatement`. + * @param ctx the parse tree + */ + exitLetStatement?: (ctx: LetStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.equals`. + * @param ctx the parse tree + */ + enterEquals?: (ctx: EqualsContext) => void; + /** + * Exit a parse tree produced by `JackParser.equals`. + * @param ctx the parse tree + */ + exitEquals?: (ctx: EqualsContext) => void; + /** + * Enter a parse tree produced by `JackParser.ifElseStatement`. + * @param ctx the parse tree + */ + enterIfElseStatement?: (ctx: IfElseStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.ifElseStatement`. + * @param ctx the parse tree + */ + exitIfElseStatement?: (ctx: IfElseStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.ifStatement`. + * @param ctx the parse tree + */ + enterIfStatement?: (ctx: IfStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.ifStatement`. + * @param ctx the parse tree + */ + exitIfStatement?: (ctx: IfStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.ifExpression`. + * @param ctx the parse tree + */ + enterIfExpression?: (ctx: IfExpressionContext) => void; + /** + * Exit a parse tree produced by `JackParser.ifExpression`. + * @param ctx the parse tree + */ + exitIfExpression?: (ctx: IfExpressionContext) => void; + /** + * Enter a parse tree produced by `JackParser.elseStatement`. + * @param ctx the parse tree + */ + enterElseStatement?: (ctx: ElseStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.elseStatement`. + * @param ctx the parse tree + */ + exitElseStatement?: (ctx: ElseStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.whileStatement`. + * @param ctx the parse tree + */ + enterWhileStatement?: (ctx: WhileStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.whileStatement`. + * @param ctx the parse tree + */ + exitWhileStatement?: (ctx: WhileStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.whileExpression`. + * @param ctx the parse tree + */ + enterWhileExpression?: (ctx: WhileExpressionContext) => void; + /** + * Exit a parse tree produced by `JackParser.whileExpression`. + * @param ctx the parse tree + */ + exitWhileExpression?: (ctx: WhileExpressionContext) => void; + /** + * Enter a parse tree produced by `JackParser.doStatement`. + * @param ctx the parse tree + */ + enterDoStatement?: (ctx: DoStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.doStatement`. + * @param ctx the parse tree + */ + exitDoStatement?: (ctx: DoStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineCall`. + * @param ctx the parse tree + */ + enterSubroutineCall?: (ctx: SubroutineCallContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineCall`. + * @param ctx the parse tree + */ + exitSubroutineCall?: (ctx: SubroutineCallContext) => void; + /** + * Enter a parse tree produced by `JackParser.subroutineId`. + * @param ctx the parse tree + */ + enterSubroutineId?: (ctx: SubroutineIdContext) => void; + /** + * Exit a parse tree produced by `JackParser.subroutineId`. + * @param ctx the parse tree + */ + exitSubroutineId?: (ctx: SubroutineIdContext) => void; + /** + * Enter a parse tree produced by `JackParser.returnStatement`. + * @param ctx the parse tree + */ + enterReturnStatement?: (ctx: ReturnStatementContext) => void; + /** + * Exit a parse tree produced by `JackParser.returnStatement`. + * @param ctx the parse tree + */ + exitReturnStatement?: (ctx: ReturnStatementContext) => void; + /** + * Enter a parse tree produced by `JackParser.expressionList`. + * @param ctx the parse tree + */ + enterExpressionList?: (ctx: ExpressionListContext) => void; + /** + * Exit a parse tree produced by `JackParser.expressionList`. + * @param ctx the parse tree + */ + exitExpressionList?: (ctx: ExpressionListContext) => void; + /** + * Enter a parse tree produced by `JackParser.expression`. + * @param ctx the parse tree + */ + enterExpression?: (ctx: ExpressionContext) => void; + /** + * Exit a parse tree produced by `JackParser.expression`. + * @param ctx the parse tree + */ + exitExpression?: (ctx: ExpressionContext) => void; + /** + * Enter a parse tree produced by `JackParser.constant`. + * @param ctx the parse tree + */ + enterConstant?: (ctx: ConstantContext) => void; + /** + * Exit a parse tree produced by `JackParser.constant`. + * @param ctx the parse tree + */ + exitConstant?: (ctx: ConstantContext) => void; + /** + * Enter a parse tree produced by `JackParser.varName`. + * @param ctx the parse tree + */ + enterVarName?: (ctx: VarNameContext) => void; + /** + * Exit a parse tree produced by `JackParser.varName`. + * @param ctx the parse tree + */ + exitVarName?: (ctx: VarNameContext) => void; + /** + * Enter a parse tree produced by `JackParser.arrayAccess`. + * @param ctx the parse tree + */ + enterArrayAccess?: (ctx: ArrayAccessContext) => void; + /** + * Exit a parse tree produced by `JackParser.arrayAccess`. + * @param ctx the parse tree + */ + exitArrayAccess?: (ctx: ArrayAccessContext) => void; + /** + * Enter a parse tree produced by `JackParser.unaryOperation`. + * @param ctx the parse tree + */ + enterUnaryOperation?: (ctx: UnaryOperationContext) => void; + /** + * Exit a parse tree produced by `JackParser.unaryOperation`. + * @param ctx the parse tree + */ + exitUnaryOperation?: (ctx: UnaryOperationContext) => void; + /** + * Enter a parse tree produced by `JackParser.groupedExpression`. + * @param ctx the parse tree + */ + enterGroupedExpression?: (ctx: GroupedExpressionContext) => void; + /** + * Exit a parse tree produced by `JackParser.groupedExpression`. + * @param ctx the parse tree + */ + exitGroupedExpression?: (ctx: GroupedExpressionContext) => void; + /** + * Enter a parse tree produced by `JackParser.booleanLiteral`. + * @param ctx the parse tree + */ + enterBooleanLiteral?: (ctx: BooleanLiteralContext) => void; + /** + * Exit a parse tree produced by `JackParser.booleanLiteral`. + * @param ctx the parse tree + */ + exitBooleanLiteral?: (ctx: BooleanLiteralContext) => void; + /** + * Enter a parse tree produced by `JackParser.unaryOperator`. + * @param ctx the parse tree + */ + enterUnaryOperator?: (ctx: UnaryOperatorContext) => void; + /** + * Exit a parse tree produced by `JackParser.unaryOperator`. + * @param ctx the parse tree + */ + exitUnaryOperator?: (ctx: UnaryOperatorContext) => void; + /** + * Enter a parse tree produced by `JackParser.binaryOperator`. + * @param ctx the parse tree + */ + enterBinaryOperator?: (ctx: BinaryOperatorContext) => void; + /** + * Exit a parse tree produced by `JackParser.binaryOperator`. + * @param ctx the parse tree + */ + exitBinaryOperator?: (ctx: BinaryOperatorContext) => void; + + visitTerminal(node: TerminalNode): void { + return; + } + visitErrorNode(node: ErrorNode): void { + return; + } + enterEveryRule(node: ParserRuleContext): void { + return; + } + exitEveryRule(node: ParserRuleContext): void { + return; + } +} diff --git a/simulator/src/jack/listener/common.ts b/simulator/src/jack/listener/common.ts new file mode 100644 index 000000000..8c7c1b16d --- /dev/null +++ b/simulator/src/jack/listener/common.ts @@ -0,0 +1,47 @@ +import { SubroutineIdContext } from "../generated/JackParser.js"; +import { LocalSymbolTable, VariableSymbol } from "../symbol.js"; + +export interface CallTypeResult { + callType: CallType; + subroutineIdText: string; + symbol?: VariableSymbol; +} + +export const CallType = { + VarMethod: 1, + LocalMethod: 2, + ClassFunctionOrConstructor: 3, +} as const; +export type CallType = (typeof CallType)[keyof typeof CallType]; + +export function getCallType( + subroutineId: SubroutineIdContext, + className: string, + localSymbolTable: LocalSymbolTable, +): CallTypeResult { + if (subroutineId.DOT() == undefined) { + //local method + return { + callType: CallType.LocalMethod, + subroutineIdText: + className + "." + (subroutineId.subroutineName()?.getText() ?? ""), + } as CallTypeResult; + } else { + // var method + const [varName, methodName] = subroutineId.getText().split("."); + const symbol = localSymbolTable.lookup(varName); + if (symbol != undefined) { + return { + callType: CallType.VarMethod, + subroutineIdText: symbol.type + "." + methodName, + symbol: symbol, + } as CallTypeResult; + } else { + // class function/ctor + return { + callType: CallType.ClassFunctionOrConstructor, + subroutineIdText: subroutineId.getText(), + } as CallTypeResult; + } + } +} diff --git a/simulator/src/jack/listener/error.listener.ts b/simulator/src/jack/listener/error.listener.ts new file mode 100644 index 000000000..dc06c87d5 --- /dev/null +++ b/simulator/src/jack/listener/error.listener.ts @@ -0,0 +1,91 @@ +import { + ANTLRErrorListener, + ATNConfigSet, + ATNSimulator, + BitSet, + DFA, + LexerNoViableAltException, + NoViableAltException, + Parser, + RecognitionException, + Recognizer, + Token, +} from "antlr4ng"; +import { JackCompilerError, LexerOrParserError } from "../error.js"; +import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; + +export class CustomErrorListener implements ANTLRErrorListener { + public errors: JackCompilerError[] = []; + syntaxError( + _recognizer: Recognizer, + offendingSymbol: S | null, + line: number, + _charPositionInLine: number, + msg: string, + e: RecognitionException | null, + ): void { + if (offendingSymbol != null || (e != null && e.offendingToken != null)) { + const t = offendingSymbol ?? (e?.offendingToken as Token); + this.errors.push( + LexerOrParserError( + { line: line, start: t.start, end: t.stop + 1 }, + msg, + ), + ); + } else if (e instanceof NoViableAltException) { + //theoretically we can't get this exception + const token = assertExists( + e.startToken ?? e.offendingToken, + "Cant find start token for NoViableAltException", + ); + this.errors.push( + LexerOrParserError( + { line: token.line, start: token.start, end: token.stop }, + msg, + ), + ); + } else if (e instanceof LexerNoViableAltException) { + this.errors.push( + LexerOrParserError( + { line: line, start: e.startIndex, end: e.startIndex + 1 }, + msg, + ), + ); + } else { + console.error("Don't know how to handle this error"); + throw new Error("Don't know how to handle this error "); + } + } + + reportAmbiguity( + _recognizer: Parser, + _dfa: DFA, + _startIndex: number, + _stopIndex: number, + _exact: boolean, + _ambigAlts: BitSet | undefined, + _configs: ATNConfigSet, + ): void { + return; + } + reportAttemptingFullContext( + _recognizer: Parser, + _dfa: DFA, + _startIndex: number, + _stopIndex: number, + _conflictingAlts: BitSet | undefined, + _configs: ATNConfigSet, + ): void { + return; + } + reportContextSensitivity( + _recognizer: Parser, + _dfa: DFA, + _startIndex: number, + _stopIndex: number, + _prediction: number, + _configs: ATNConfigSet, + ): void { + return; + } +} diff --git a/simulator/src/jack/listener/global.symbol.listener.ts b/simulator/src/jack/listener/global.symbol.listener.ts new file mode 100644 index 000000000..172b83f07 --- /dev/null +++ b/simulator/src/jack/listener/global.symbol.listener.ts @@ -0,0 +1,100 @@ +import { + ClassDeclarationContext, + SubroutineDeclarationContext, + VarNameInDeclarationContext, +} from "../generated/JackParser.js"; +import { + ruleContextToSpan, + DuplicatedClassError, + DuplicatedSubroutineError, + JackCompilerError, +} from "../error.js"; +import { + GenericSymbol, + GlobalSymbolTable, + SubroutineInfo, + SubroutineType, +} from "../symbol.js"; +import { builtInSymbols } from "../builtins.js"; +import { JackParserListener } from "../generated/JackParserListener.js"; + +/** + * Creates global symbol table that contains built-in functions and found classes and subroutines + */ +export class GlobalSymbolTableListener extends JackParserListener { + // key can be class or . + public globalSymbolTable: GlobalSymbolTable = structuredClone(builtInSymbols); + public className = ""; + public errors: JackCompilerError[] = []; + private subRoutineInfo: SubroutineInfo = {} as SubroutineInfo; + private subroutineVarsCount = 0; + private stopProcessingSubroutines = false; + private subroutineId = ""; + public filename = ""; + override enterClassDeclaration = (ctx: ClassDeclarationContext) => { + const classNameCtx = ctx.className(); + const id = classNameCtx.IDENTIFIER(); + const className = id.getText(); + if (this.globalSymbolTable[className] != undefined) { + if (classNameCtx.stop == null || classNameCtx.stop?.stop == null) + throw new Error("Stop token should not be null"); + if (classNameCtx.start == null) { + throw new Error("Start token should not be null"); + } + const e = DuplicatedClassError( + ruleContextToSpan(classNameCtx), + className, + ); + this.errors.push(e); + return; + } + this.globalSymbolTable[className] = {} as GenericSymbol; + this.className = className; + }; + + override enterSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { + let subroutineType: SubroutineType; + if (ctx.subroutineType().CONSTRUCTOR() != null) { + subroutineType = SubroutineType.Constructor; + } else if (ctx.subroutineType().METHOD() != null) { + subroutineType = SubroutineType.Method; + } else if (ctx.subroutineType().FUNCTION() != null) { + subroutineType = SubroutineType.Function; + } else { + throw new Error("Invalid subroutine type"); + } + const subroutineWithoutTypeCtx = ctx.subroutineDecWithoutType(); + const nameCtx = subroutineWithoutTypeCtx.subroutineName(); + const subroutineName = nameCtx.IDENTIFIER().getText(); + const id = this.className + "." + subroutineName; + if (this.globalSymbolTable[id] != undefined) { + if (nameCtx.start == null) { + throw new Error("Start token should not be null"); + } + this.errors.push( + DuplicatedSubroutineError(ruleContextToSpan(nameCtx), subroutineName), + ); + this.stopProcessingSubroutines = true; + } else { + this.subroutineId = id; + const params = subroutineWithoutTypeCtx.parameterList().parameter(); + this.subRoutineInfo = { + type: subroutineType, + paramsCount: params.length, + }; + this.subroutineVarsCount = 0; + this.stopProcessingSubroutines = false; + } + }; + override enterVarNameInDeclaration = (_ctx: VarNameInDeclarationContext) => { + if (this.stopProcessingSubroutines) return; + this.subroutineVarsCount++; + }; + override exitSubroutineDeclaration = (_ctx: SubroutineDeclarationContext) => { + if (this.stopProcessingSubroutines) return; + this.subRoutineInfo.localVarsCount = this.subroutineVarsCount; + this.globalSymbolTable[this.subroutineId] = { + subroutineInfo: this.subRoutineInfo, + }; + }; +} diff --git a/simulator/src/jack/listener/global.symbol.table.listener.test.ts b/simulator/src/jack/listener/global.symbol.table.listener.test.ts new file mode 100644 index 000000000..99c1ab4fc --- /dev/null +++ b/simulator/src/jack/listener/global.symbol.table.listener.test.ts @@ -0,0 +1,121 @@ +import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; +import { NodeFileSystemAdapter } from "@davidsouther/jiffies/lib/esm/fs_node.js"; +import path from "path"; +import { builtInSymbols } from "../builtins"; +import { JackCompilerErrorType } from "../error"; +import { createSubroutineSymbol, SubroutineType } from "../symbol"; +import { + getTestResourcePath, + listenToTheTree, + parseJackFile, + parseJackText, +} from "../test.helper"; +import { GlobalSymbolTableListener } from "./global.symbol.listener"; + +describe("Jack global symbol table listener", () => { + const jestConsole = console; + let fs: FileSystem; + beforeEach(() => { + global.console = require("console"); + fs = new FileSystem(new NodeFileSystemAdapter()); + }); + + afterEach(() => { + global.console = jestConsole; + }); + + test("should fail on duplicated subroutine", () => { + const input = ` + class A { + function void f(){ + return; + } + function int f(){ + return 1; + } + }`; + testJackGlobalSymbolListener(input, "DuplicatedSubroutineError"); + }); + + test("duplicated class", () => { + const input = ` + class A { + }`; + const globalSymbolTableListener = new GlobalSymbolTableListener(); + testJackGlobalSymbolListener(input, undefined, globalSymbolTableListener); + testJackGlobalSymbolListener( + input, + "DuplicatedClassError", + globalSymbolTableListener, + ); + }); + test("duplicated built in class", () => { + const input = ` + class Math { + }`; + testJackGlobalSymbolListener(input, "DuplicatedClassError"); + }); + test("basic", async () => { + const expected = { + ...builtInSymbols, + Fraction: {}, + "Fraction.new": createSubroutineSymbol(2, SubroutineType.Constructor, 0), + "Fraction.reduce": createSubroutineSymbol(0, SubroutineType.Method, 1), + "Fraction.getNumerator": createSubroutineSymbol( + 0, + SubroutineType.Method, + 0, + ), + "Fraction.getDenominator": createSubroutineSymbol( + 0, + SubroutineType.Method, + 0, + ), + "Fraction.plus": createSubroutineSymbol(1, SubroutineType.Method, 1), + "Fraction.dispose": createSubroutineSymbol(0, SubroutineType.Method, 0), + "Fraction.print": createSubroutineSymbol(0, SubroutineType.Method, 0), + "Fraction.gcd": createSubroutineSymbol(2, SubroutineType.Function, 1), + Main: {}, + "Main.main": createSubroutineSymbol(0, SubroutineType.Function, 3), + }; + let globalSymbolsListener = new GlobalSymbolTableListener(); + + const testFolder = getTestResourcePath("Fraction"); + + const filteredFiles = [...(await fs.readdir(testFolder))] + .filter((file) => file.endsWith(".jack")) + .map((file) => path.join(testFolder, file)); + for (const filePath of filteredFiles) { + const tree = parseJackFile(filePath); + globalSymbolsListener = listenToTheTree(tree, globalSymbolsListener); + } + expect(globalSymbolsListener.globalSymbolTable).toEqual(expected); + }); +}); +function testJackGlobalSymbolListener( + input: string, + expectedError?: T, + globalSymbolTableListener = new GlobalSymbolTableListener(), +) { + const tree = parseJackText(input); + listenToTheTree(tree, globalSymbolTableListener); + const errors = globalSymbolTableListener.errors; + if (expectedError) { + if (errors.length > 1) { + console.error("Errors", errors); + } + try { + expect(globalSymbolTableListener.errors.length).toBe(1); + expect(globalSymbolTableListener.errors[0].type).toBe(expectedError); + } catch (e) { + throw new Error( + `Expected error ${expectedError} but got '` + + JSON.stringify(globalSymbolTableListener.errors) + + "'", + ); + } + } else { + if (errors.length != 0) + throw new Error("Didn't expect any errors but got " + errors.join("\n")); + } +} diff --git a/simulator/src/jack/listener/validator.listener.test.ts b/simulator/src/jack/listener/validator.listener.test.ts new file mode 100644 index 000000000..810c58276 --- /dev/null +++ b/simulator/src/jack/listener/validator.listener.test.ts @@ -0,0 +1,824 @@ +import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; +import { NodeFileSystemAdapter } from "@davidsouther/jiffies/lib/esm/fs_node.js"; +import { + createSubroutineSymbol, + GenericSymbol, + SubroutineType, +} from "../symbol"; +import { + getTestResourcePath, + listenToTheTree, + parseJackFile, + parseJackText, + testResourceDirs, +} from "../test.helper"; +import path from "path"; +import { ProgramContext } from "../generated/JackParser"; +import { JackCompilerErrorType } from "../error"; +import { GlobalSymbolTableListener } from "./global.symbol.listener"; +import { ValidatorListener } from "./validator.listener"; +import { CustomErrorListener } from "./error.listener"; +describe("Jack validator listener", () => { + const jestConsole = console; + let fs: FileSystem; + beforeEach(() => { + global.console = require("console"); + fs = new FileSystem(new NodeFileSystemAdapter()); + }); + + afterEach(() => { + global.console = jestConsole; + }); + + function genericSymbol( + type?: SubroutineType, + paramsCount?: number, + ): GenericSymbol { + if (type != undefined && paramsCount != undefined) { + return createSubroutineSymbol(paramsCount, type); + } else { + return {} as GenericSymbol; + } + } + + const duplicateVarClassBodies = [ + ["static", " static int a, a;"], + ["field", " field int a, a;"], + ["static and field", " static int a; field boolean a;"], + ["function args", " function void a(int a, int a){return;}"], + [ + "function var", + ` function void a(){ + var boolean a, a; + return; + }`, + ], + [ + "function var with different types", + ` function void a(){ + var boolean a; + var int a; + return; + }`, + ], + ]; + test.concurrent.each(duplicateVarClassBodies)( + "duplicated %s", + (testName, classBody) => { + testValidator( + ` + class Main { + ${classBody} + }`, + "DuplicatedVariableError", + ); + }, + ); + + /** + * Undeclared var + */ + test("let - undeclared variable ", () => { + testValidator( + ` + class Main { + function void a(){ + let b=1; + return; + } + }`, + "UndeclaredVariableError", + ); + }); + + test("call function - undeclared variable ", () => { + testValidator( + `class Main { + function void b(int a){ + return; + } + function void a(){ + do Main.b(a); + return; + } + }`, + "UndeclaredVariableError", + { + Main: genericSymbol(), + "Main.b": genericSymbol(SubroutineType.Function, 1), + "Main.a": genericSymbol(SubroutineType.Function, 1), + }, + ); + }); + + test("if - undeclared variable ", () => { + testValidator( + `class Main { + function void a(){ + if(a=0){ + return; + }else { + return; + } + } + }`, + "UndeclaredVariableError", + ); + }); + + /** + * Unknown class + */ + test("Unknown class for subroutine return type ", () => { + testValidator( + ` + class Main { + function void b(int a){ + var D d; + return; + } + }`, + "UnknownClassError", + ); + }); + + test("Known type for subroutine return type ", () => { + testValidator( + ` + class Main { + function D b(int a){ + return D.new(); + } + }`, + undefined, + { + D: genericSymbol(), + "D.new": genericSymbol(SubroutineType.Constructor, 0), + }, + ); + }); + test("Arg Unknown class ", () => { + testValidator( + ` + class Main { + function void b(D a){ + return; + } + }`, + "UnknownClassError", + ); + }); + + test("Arg known type ", () => { + testValidator( + ` + + class Main { + function void b(D a){ + return; + } + }`, + undefined, + { D: genericSymbol() }, + ); + }); + test("var Unknown class", () => { + testValidator( + ` + class Main { + function void b(){ + var D d; + return; + } + }`, + "UnknownClassError", + ); + }); + test("var known type", () => { + testValidator( + ` + class Main { + function void b(){ + var D d; + return; + } + }`, + undefined, + { D: genericSymbol() }, + ); + }); + test("field Unknown class", () => { + testValidator( + ` + class Main { + field T t; + }`, + "UnknownClassError", + ); + }); + test("field known type", () => { + testValidator( + ` + class Main { + field T t; + }`, + undefined, + { T: genericSymbol() }, + ); + }); + test("static field Unknown class", () => { + testValidator( + ` + class Main { + static T t; + }`, + "UnknownClassError", + ); + }); + test("static field known type", () => { + testValidator( + ` + class Main { + static T t; + }`, + undefined, + { T: genericSymbol() }, + ); + }); + + /** + * Incorrect return type + */ + + test("non void subroutine must return a value", () => { + testValidator( + ` + class Main { + function int a(){ + return; + } + }`, + "NonVoidFunctionNoReturnError", + ); + }); + + test("void subroutine must return not return a value", () => { + testValidator( + ` + class Main { + function void a(){ + return 1; + } + }`, + "VoidSubroutineReturnsValueError", + ); + }); + /** + * `Subroutine ${subroutine.name.value}: not all code paths return a value` + */ + + test("if missing return", () => { + testValidator( + ` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + + }else{ + return; + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + test("else missing return ", () => { + testValidator( + ` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + return; + }else{ + + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + + test("while missing return", () => { + testValidator( + ` + class Main { + function int a(){ + var int a; + let a=0; + while(a<10){ + + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + + test(" missing return after while", () => { + testValidator( + ` + class Main { + function int a(){ + var int a; + let a=0; + while(a<10){ + return 0; + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + + test("nested if missing return", () => { + testValidator( + ` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + if(a=1){ + return; + }else { + + } + }else{ + return; + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + test("nested if missing return 2", () => { + testValidator( + ` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + if(a=1){ + + }else { + return; + } + }else{ + return; + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + + test("nested if missing return 3", () => { + testValidator( + ` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + if(a=1){ + return; + }else { + return; + } + }else{ + + } + } + }`, + "SubroutineNotAllPathsReturnError", + ); + }); + test("should be valid", () => { + testValidator(` + class Main { + function void a(){ + var int a; + let a=0; + if(a=0){ + if(a=1){ + }else { + } + } + return; + } + }`); + }); + /** + * Validate function call + */ + test("calling undefined subroutine", () => { + testValidator( + ` + class Main { + function void b(){ + do Main.c(); + return; + } + }`, + "UnknownSubroutineCallError", + ); + }); + + test("incorrect number of parameters when calling a function", () => { + testValidator( + ` + class Main { + function void a(int a, int b){ + return; + } + function void b(){ + do Main.a(1); + return; + } + }`, + "IncorrectParamsNumberInSubroutineCallError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 2), + "Main.b": genericSymbol(SubroutineType.Function, 2), + }, + ); + }); + + test("call var method ", () => { + testValidator( + ` + class Main { + constructor Main new(){ + return this; + } + function void a(){ + var Main m; + let m = Main.new(); + do m.b(); + return; + } + method void b(){ + return; + } + }`, + undefined, + { + Main: genericSymbol(), + "Main.new": genericSymbol(SubroutineType.Constructor, 0), + "Main.a": genericSymbol(SubroutineType.Function, 0), + "Main.b": genericSymbol(SubroutineType.Method, 0), + }, + ); + }); + test("call local method ", () => { + testValidator( + ` + class Main { + method void a(){ + do b(); + return; + } + method void b(){ + return; + } + }`, + undefined, + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Method, 0), + "Main.b": genericSymbol(SubroutineType.Method, 0), + }, + ); + }); + + /** + * - `Method ${className}.${subroutineName} was called as a function/constructor` + */ + test("method called as a function/constructor", () => { + testValidator( + ` + class Main { + function void b(){ + do Main.c(); + return; + } + method void c(){ + return; + } + }`, + "MethodCalledAsFunctionError", + { + Main: genericSymbol(), + "Main.b": genericSymbol(SubroutineType.Function, 0), + "Main.c": genericSymbol(SubroutineType.Method, 0), + }, + ); + }); + test("function/ctor called as a method", () => { + testValidator( + ` + class Main { + function void b(){ + do c(); + return; + } + function void c(){ + return; + } + }`, + "FunctionCalledAsMethodError", + { + Main: genericSymbol(), + "Main.b": genericSymbol(SubroutineType.Function, 0), + "Main.c": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("incorrect return type in constructor", () => { + testValidator( + ` + class Main { + constructor D new(){ + return this; + } + }`, + "IncorrectConstructorReturnTypeError", + { + Main: genericSymbol(), + D: genericSymbol(), + }, + ); + }); + test("unreachable code", () => { + testValidator( + ` + class Main { + constructor Main new(){ + return this; + let a=0; + let a=0; + let a=0; + } + }`, + "UnreachableCodeError", + { + Main: genericSymbol(), + "Main.new": genericSymbol(SubroutineType.Constructor, 0), + }, + ); + }); + test("A constructor must return 'this'", () => { + testValidator( + ` + class Main { + constructor Main new(){ + return 1; + } + }`, + "ConstructorMushReturnThisError", + { + Main: genericSymbol(), + "Main.new": genericSymbol(SubroutineType.Constructor, 0), + }, + ); + }); + test("Let statement - expected string literal ", () => { + testValidator( + ` + class Main { + function void a(){ + var String foo; + let foo = 1; + return; + } + }`, + "WrongLiteralTypeError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + String: genericSymbol(), + }, + ); + }); + test("Let statement - expected boolean literal ", () => { + testValidator( + ` + class Main { + function void a(){ + var boolean foo; + let foo = 1; + return; + } + }`, + "WrongLiteralTypeError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + + test("Let statement - expected int literal ", () => { + testValidator( + ` + class Main { + function void a(){ + var int foo; + let foo = "asb"; + return; + } + }`, + "WrongLiteralTypeError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("integer constant value is too big", () => { + testValidator( + ` + class Main { + function void a(){ + var int foo; + let foo = 33000; + return; + } + }`, + "IntLiteralIsOutOfRangeError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("integer constant value is too small", () => { + testValidator( + ` + class Main { + function void a(){ + var int foo; + let foo = -33000; + return; + } + }`, + "IntLiteralIsOutOfRangeError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("A field can not be referenced in a function", () => { + testValidator( + ` + class Main { + field int a; + function void a(){ + let a = 1; + return; + } + }`, + "FieldCantBeReferencedInFunctionError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("A static field can be referenced in a function", () => { + testValidator( + ` + class Main { + static int a; + function void a(){ + let a = 1; + return; + } + }`, + undefined, + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("this can't be referenced in a function", () => { + testValidator( + ` + class Main { + function void a(){ + var Main m; + let m = this; + return; + } + }`, + "ThisCantBeReferencedInFunctionError", + { + Main: genericSymbol(), + "Main.a": genericSymbol(SubroutineType.Function, 0), + }, + ); + }); + test("class name doesn't match filename", () => { + testValidator( + `class A {}`, + "FilenameDoesntMatchClassNameError", + { + A: genericSymbol(), + }, + "B", + ); + }); + + //validate files + test.each(testResourceDirs)("%s", async (dir: string) => { + await testJackDir(fs, getTestResourcePath(dir)); + }); +}); + +async function testJackDir(fs: FileSystem, testFolder: string): Promise { + const files = [...(await fs.readdir(testFolder))] + .filter((file) => file.endsWith(".jack")) + .map((file) => path.join(testFolder, file)); + const trees: Record = {}; + const globalSymbolsListener: GlobalSymbolTableListener = + new GlobalSymbolTableListener(); + for (const filePath of files) { + const tree = parseJackFile(filePath); + trees[filePath] = tree; + listenToTheTree(tree, globalSymbolsListener); + expect(globalSymbolsListener.errors).toEqual([]); + } + for (const filepath of Object.keys(trees)) { + const tree = trees[filepath]; + const validatorListener = listenToTheTree( + tree, + new ValidatorListener(globalSymbolsListener.globalSymbolTable), + ); + expect(validatorListener.errors).toEqual([]); + } +} + +function testValidator( + src: string, + expectedError?: T, + globalSymbolTable: Record = {}, + filename?: string, +) { + const errorListener = new CustomErrorListener(); + const tree = parseJackText(src, errorListener); + const listener = + filename != null + ? new ValidatorListener(globalSymbolTable, filename) + : new ValidatorListener(globalSymbolTable); + const validator = listenToTheTree(tree, listener); + if (expectedError) { + if (validator.errors.length > 1) { + console.error("Errors", validator.errors); + } + try { + expect(validator.errors.length).toBe(1); + expect(validator.errors[0].type).toBe(expectedError); + } catch (e) { + throw new Error( + `Expected error ${expectedError} but got '` + + JSON.stringify(validator.errors) + + "'", + ); + } + } else { + if (validator.errors.length != 0) + throw new Error( + "Didn't expect any errors but got " + validator.errors.join("\n"), + ); + } +} + +/** + * TODO: + * Ideas for improvement - + * - Show "Expected class name, subroutine name, field, parameter or local or static variable name" instead of "expecting IDENTIFIER" + * - Show "Expected subroutine return type followed by a subroutine name" instead of "expecting IDENTIFIER" + * - Expected subroutine name in call + * - a numeric value is illegal here when using non numeric vars + * - validate function call - when using literal in call validate the type + * add validation for assigning from void function call + * add rule to forbid var use before assignment + */ diff --git a/simulator/src/jack/listener/validator.listener.ts b/simulator/src/jack/listener/validator.listener.ts new file mode 100644 index 000000000..86938149a --- /dev/null +++ b/simulator/src/jack/listener/validator.listener.ts @@ -0,0 +1,553 @@ +import { TerminalNode } from "antlr4ng"; +import { builtInTypes, intRange } from "../builtins.js"; +import { + ConstructorMushReturnThisError, + DuplicatedVariableError, + FieldCantBeReferencedInFunctionError, + FilenameDoesntMatchClassNameError, + FunctionCalledAsMethodError, + IncorrectConstructorReturnTypeError, + IncorrectParamsNumberInSubroutineCallError, + IntLiteralIsOutOfRangeError, + JackCompilerError, + MethodCalledAsFunctionError, + NonVoidFunctionNoReturnError, + ruleContextToSpan, + SubroutineNotAllPathsReturnError, + terminalNodeToSpan, + ThisCantBeReferencedInFunctionError, + UndeclaredVariableError, + UnknownClassError, + UnknownSubroutineCallError, + UnreachableCodeError, + VoidSubroutineReturnsValueError, + WrongLiteralTypeError, +} from "../error.js"; +import { + ClassDeclarationContext, + ClassVarDecContext, + ConstantContext, + ElseStatementContext, + FieldNameContext, + IfStatementContext, + LetStatementContext, + ParameterContext, + RBraceContext, + ReturnStatementContext, + StatementContext, + SubroutineCallContext, + SubroutineDeclarationContext, + SubroutineDecWithoutTypeContext, + VarDeclarationContext, + VarNameContext, + VarNameInDeclarationContext, + VarTypeContext, + WhileStatementContext, +} from "../generated/JackParser.js"; +import { JackParserListener } from "../generated/JackParserListener.js"; +import { + GenericSymbol, + LocalSymbolTable, + ScopeType, + SubroutineType, +} from "../symbol.js"; +import { CallType, getCallType } from "./common.js"; +import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; +/** + * Validates Jack file + */ +export class ValidatorListener extends JackParserListener { + private localSymbolTable: LocalSymbolTable = new LocalSymbolTable(); + private subroutineShouldReturnVoidType = false; + private controlFlowGraphNode: BinaryTreeNode = new BinaryTreeNode(); + private subroutineName = ""; + private className = ""; + private stopProcessingErrorsInThisScope = false; + private subroutineType?: SubroutineType; + constructor( + private globalSymbolTable: Record, + private filename?: string, + public errors: JackCompilerError[] = [], + ) { + super(); + } + + override enterClassDeclaration = (ctx: ClassDeclarationContext) => { + const className = ctx.className(); + const newName = className.getText(); + if (this.className != "") { + throw new Error("Cannot change class name"); + } + this.className = newName; + if (this.filename != null && this.filename != this.className) { + if (className.start == null) { + throw new Error("Start token should not be null"); + } + this.errors.push( + FilenameDoesntMatchClassNameError( + ruleContextToSpan(ctx.className()), + this.filename, + this.className, + ), + ); + } + ctx.localSymbolTable = this.localSymbolTable; + }; + + override enterClassVarDec = (ctx: ClassVarDecContext) => { + let scope: ScopeType; + if (ctx.STATIC() != null) { + scope = ScopeType.Static; + } else if (ctx.FIELD() != null) { + scope = ScopeType.This; + } else { + throw new Error("Unknown field modifier "); + } + const type = ctx.fieldList().varType().getText(); + ctx + .fieldList() + .fieldName() + .forEach((field: FieldNameContext) => { + this.localSymbolTableAdd( + field.IDENTIFIER(), + scope, + field.getText(), + type, + ); + }); + }; + override enterSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { + if (ctx.subroutineType().CONSTRUCTOR() != null) { + this.subroutineType = SubroutineType.Constructor; + if ( + ctx.subroutineDecWithoutType().subroutineReturnType().getText() !== + this.className + ) { + this.addError( + IncorrectConstructorReturnTypeError( + ruleContextToSpan( + ctx.subroutineDecWithoutType().subroutineReturnType(), + ), + ), + ); + } + } else if (ctx.subroutineType().FUNCTION() != null) { + this.subroutineType = SubroutineType.Function; + } else if (ctx.subroutineType().METHOD != null) { + this.subroutineType = SubroutineType.Method; + } else { + throw new Error("Unknown subroutine type "); + } + }; + override enterSubroutineDecWithoutType = ( + ctx: SubroutineDecWithoutTypeContext, + ) => { + const returnType = ctx.subroutineReturnType(); + this.subroutineShouldReturnVoidType = returnType.VOID() != null; + this.controlFlowGraphNode = new BinaryTreeNode(); + this.subroutineName = ctx.subroutineName().getText(); + }; + + override enterParameter = (ctx: ParameterContext) => { + const name = ctx.parameterName().getText(); + if (this.localSymbolTable.lookup(name)) { + this.addError( + DuplicatedVariableError(ruleContextToSpan(ctx.parameterName()), name), + ); + } else { + this.localSymbolTable.defineArgument( + name, + ctx.varType().getText(), + this.subroutineType == SubroutineType.Method, + ); + } + }; + //Var + override enterVarType = (ctx: VarTypeContext) => { + const id = ctx.IDENTIFIER(); + if (id != null) { + const type = id.getText() ?? ""; + if (this.globalSymbolTable[type] == null) { + this.addError(UnknownClassError(terminalNodeToSpan(id), type)); + } + } + }; + + override enterVarDeclaration = (ctx: VarDeclarationContext) => { + const type = ctx.varType().getText(); + ctx + .varNameInDeclaration() + .forEach((nameCtx: VarNameInDeclarationContext) => { + this.localSymbolTableAdd( + nameCtx.IDENTIFIER(), + ScopeType.Local, + nameCtx.getText(), + type, + ); + }); + }; + + /** + * Var name when doing some actions - do statement, let ... We have a different rule for a var name that is used in declaration + */ + override enterVarName = (ctx: VarNameContext) => { + const symbol = this.localSymbolTable.lookup(ctx.getText()); + if (symbol == undefined) { + this.addError( + UndeclaredVariableError( + terminalNodeToSpan(ctx.IDENTIFIER()), + ctx.getText(), + ), + ); + } else if ( + this.subroutineType == SubroutineType.Function && + symbol.scope == ScopeType.This + ) { + this.addError( + FieldCantBeReferencedInFunctionError( + terminalNodeToSpan(ctx.IDENTIFIER()), + ), + ); + } + }; + + override enterConstant = (ctx: ConstantContext) => { + const thisLiteral = ctx.THIS_LITERAL(); + if (thisLiteral != null && this.subroutineType == SubroutineType.Function) { + this.addError( + ThisCantBeReferencedInFunctionError(terminalNodeToSpan(thisLiteral)), + ); + } + }; + + override enterStatement = (ctx: StatementContext) => { + if (this.controlFlowGraphNode.returns == true) { + this.addError(UnreachableCodeError(ruleContextToSpan(ctx))); + this.stopProcessingErrorsInThisScope = true; + } + }; + override enterRBrace = (_ctx: RBraceContext) => { + this.stopProcessingErrorsInThisScope = false; + }; + /** + * Control flow + */ + override enterWhileStatement = (_ctx: WhileStatementContext) => { + this.controlFlowGraphNode = this.controlFlowGraphNode.left = + new BinaryTreeNode(this.controlFlowGraphNode); + }; + + override exitWhileStatement = (_ctx: WhileStatementContext) => { + if (this.controlFlowGraphNode?.parent != null) { + this.controlFlowGraphNode = this.controlFlowGraphNode.parent; + } + }; + override enterIfStatement = (_ctx: IfStatementContext) => { + this.controlFlowGraphNode = this.controlFlowGraphNode.left = + new BinaryTreeNode(this.controlFlowGraphNode); + }; + override exitIfStatement = (_ctx: IfStatementContext) => { + if (this.controlFlowGraphNode?.parent != null) { + this.controlFlowGraphNode = this.controlFlowGraphNode.parent; + } + }; + override enterElseStatement = (_ctx: ElseStatementContext) => { + this.controlFlowGraphNode = this.controlFlowGraphNode.right = + new BinaryTreeNode(this.controlFlowGraphNode); + }; + override exitElseStatement = (_ctx: ElseStatementContext) => { + if (this.controlFlowGraphNode?.parent != null) { + this.controlFlowGraphNode = this.controlFlowGraphNode.parent; + } + }; + override enterLetStatement = (ctx: LetStatementContext) => { + const varName = ctx.varName()?.getText(); + const constCtx = ctx.expression()?.constant(); + //corresponding literal type check + if ( + varName != null && + constCtx != null && + this.localSymbolTable.lookup(varName) && + constCtx.NULL_LITERAL() == null + ) { + const symbol = this.localSymbolTable.lookup(varName); + if (symbol != null && literalTypes.indexOf(symbol.type) != -1) { + let expectedLiteralType = symbol.type; + if (expectedLiteralType === "char") { + expectedLiteralType = "int"; + } + const constantCtx = ctx.expression()?.constant(); + let actualType = ""; + if (constantCtx?.INTEGER_LITERAL() != null) { + actualType = "int"; + const intLiteral = constantCtx?.INTEGER_LITERAL(); + const value = constantCtx?.INTEGER_LITERAL()?.getText(); + if ( + constCtx != null && + intLiteral != null && + value != null && + parseInt(value) > intRange.max + ) { + this.addError( + IntLiteralIsOutOfRangeError( + terminalNodeToSpan(intLiteral), + parseInt(value), + intRange.min, + intRange.max, + ), + ); + } + } else if (constCtx?.STRING_LITERAL() != null) { + actualType = "String"; + } else if (constCtx?.booleanLiteral() != null) { + actualType = "boolean"; + } else { + throw new Error(`Unknown literal type ${expectedLiteralType}`); + } + if (actualType != expectedLiteralType) { + this.addError( + WrongLiteralTypeError( + ruleContextToSpan(constantCtx ?? ctx), + symbol.type, + actualType, + ), + ); + } + } + } + //int min value check + const unaryOp = ctx.expression()?.unaryOperation(); + if ( + varName && + unaryOp != null && + unaryOp.unaryOperator().MINUS() !== null && + unaryOp.expression()?.constant() != null && + unaryOp.expression()?.constant()?.INTEGER_LITERAL() !== null + ) { + const value = parseInt( + unaryOp.expression()?.constant()?.INTEGER_LITERAL()?.getText() ?? "0", + ); + if (-value < intRange.min) { + this.addError( + IntLiteralIsOutOfRangeError( + terminalNodeToSpan( + assertExists(unaryOp.expression()?.constant()?.INTEGER_LITERAL()), + ), + value, + intRange.min, + intRange.max, + ), + ); + } + } + }; + + override enterSubroutineCall = (ctx: SubroutineCallContext) => { + //check if variable exists with the name before dot + const subroutineId = ctx.subroutineId(); + const { callType, subroutineIdText } = getCallType( + subroutineId, + this.className, + this.localSymbolTable, + ); + + const symbol = this.globalSymbolTable[subroutineIdText]; + if (symbol == undefined) { + this.addError( + UnknownSubroutineCallError( + ruleContextToSpan(subroutineId.subroutineName()), + subroutineId.subroutineName().getText(), + subroutineId.className()?.getText(), + ), + ); + } else { + //method called as a function + if ( + symbol.subroutineInfo?.type == SubroutineType.Method && + callType == CallType.ClassFunctionOrConstructor + ) { + this.addError( + MethodCalledAsFunctionError( + ruleContextToSpan(subroutineId.subroutineName()), + subroutineId.subroutineName().getText(), + ), + ); + } + // function called as a method + else if ( + symbol.subroutineInfo?.type == SubroutineType.Function && + callType == CallType.LocalMethod + ) { + this.addError( + FunctionCalledAsMethodError( + ruleContextToSpan(subroutineId.subroutineName()), + subroutineId.subroutineName().getText(), + ), + ); + } else { + //check parameter count + const l = ctx.expressionList()?.expression().length; + if (symbol.subroutineInfo?.paramsCount != l) { + if (symbol.subroutineInfo == null) + throw new Error("Subroutine info cannot be null"); + if ( + ctx.expressionList() != null && + ctx.expressionList().start != null + ) { + this.addError( + IncorrectParamsNumberInSubroutineCallError( + ruleContextToSpan(ctx.expressionList()), + subroutineId.getText(), + assertExists(symbol.subroutineInfo).paramsCount, + l, + ), + ); + } else { + const start = ctx.LPAREN().symbol; + const stop = ctx.RPAREN().symbol; + + this.addError( + IncorrectParamsNumberInSubroutineCallError( + { line: start.line, start: start.start, end: stop.stop + 1 }, + subroutineId.getText(), + symbol.subroutineInfo?.paramsCount ?? 0, + l, + ), + ); + } + } + } + } + }; + override enterReturnStatement = (ctx: ReturnStatementContext) => { + const returnsVoid = ctx.expression() == null; + if (returnsVoid && !this.subroutineShouldReturnVoidType) { + this.addError(NonVoidFunctionNoReturnError(ruleContextToSpan(ctx))); + } + if (!returnsVoid && this.subroutineShouldReturnVoidType) { + this.addError(VoidSubroutineReturnsValueError(ruleContextToSpan(ctx))); + } + this.controlFlowGraphNode.returns = true; + if (this.subroutineType == SubroutineType.Constructor) { + if ( + returnsVoid || + (ctx.expression()?.expression().length ?? 0) > 1 || + ctx.expression()?.constant() == null || + ctx.expression()?.constant()?.THIS_LITERAL() == null + ) { + this.addError(ConstructorMushReturnThisError(ruleContextToSpan(ctx))); + } + } + }; + + override exitSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { + if (!this.controlFlowGraphNode.returns) { + this.addError( + //TODO: add exact place that doesn't return + SubroutineNotAllPathsReturnError( + ruleContextToSpan(ctx.subroutineType()), + this.subroutineName, + ), + ); + } + this.subroutineType = undefined; + ctx.symbols = this.localSymbolTable.popStack(); + }; + + override exitClassDeclaration = (_ctx: ClassDeclarationContext) => { + while (this.controlFlowGraphNode?.parent != undefined) { + this.controlFlowGraphNode = this.controlFlowGraphNode.parent; + } + }; + + //Utils + + localSymbolTableAdd( + identifierCtx: TerminalNode, + scope: ScopeType, + name: string, + type: string, + ) { + if (this.localSymbolTable.lookup(name)) { + this.addError( + DuplicatedVariableError(terminalNodeToSpan(identifierCtx), name), + ); + } else { + this.localSymbolTable.define(scope, name, type); + } + } + addError(error: T) { + if (!this.stopProcessingErrorsInThisScope) this.errors.push(error); + } +} + +class BinaryTreeNode { + private _returns?: boolean; + constructor( + public parent?: BinaryTreeNode, + public left?: BinaryTreeNode, + public right?: BinaryTreeNode, + ) {} + + public get returns(): boolean { + if (this._returns) { + return this._returns; + } else if (this.right == undefined && this.left == undefined) { + return false; + } else if (this.right != undefined && this.left != undefined) { + return this.left.returns && this.right.returns; + } else if (this.left != undefined) { + return false; + } else { + throw new Error("Something went wrong - CFG has only right subtree"); + } + } + + public set returns(_returns: boolean) { + this._returns = _returns; + } + print() { + console.log("Branch returns value"); + console.log("."); + console.log(this.printBT()); + } + + printBT(prefix = "", side: Side = Side.Left) { + let res = ""; + if (this._returns) { + res += this.#pad(side); + res += " " + this._returns + "\n"; + return res; + } else { + if (this.right == undefined && this.left == undefined) { + res += this.#pad(side); + res += " " + false + "\n"; + } else { + res += this.left?.printBT( + side == Side.Left ? "| " : " ", + Side.Left, + ); + if (this.right) { + res += prefix; + res += this.right?.printBT( + side == Side.Left ? "|\t" : "\t", + Side.Right, + ); + } else { + res += "\n"; + } + } + } + return res; + } + #pad(side: Side): string { + return side == Side.Left ? "├──" : "└──"; + } +} + +const Side = { + Left: 1, + Right: 2, +} as const; +export type Side = (typeof Side)[keyof typeof Side]; +const literalTypes = [...builtInTypes, "String"]; diff --git a/simulator/src/jack/listener/vm.writer.listener.ts b/simulator/src/jack/listener/vm.writer.listener.ts new file mode 100644 index 000000000..f111a5d8e --- /dev/null +++ b/simulator/src/jack/listener/vm.writer.listener.ts @@ -0,0 +1,315 @@ +import { JackParserListener } from "../generated/JackParserListener.js"; +import { + ArrayAccessContext, + ClassDeclarationContext, + ConstantContext, + DoStatementContext, + ExpressionContext, + IfElseStatementContext, + IfExpressionContext, + IfStatementContext, + LetStatementContext, + ReturnStatementContext, + SubroutineCallContext, + SubroutineDeclarationContext, + WhileExpressionContext, + WhileStatementContext, +} from "../generated/JackParser.js"; +import { + GenericSymbol, + LocalSymbolTable, + scopeTypeToString, + VariableSymbol, +} from "../symbol.js"; +import { CallType, getCallType } from "./common.js"; +import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; + +const binaryOperationToVmCmd: Record = { + "+": "add", + "-": "sub", + "*": "call Math.multiply 2", + "/": "call Math.divide 2", + "&": "and", + "|": "or", + "<": "lt", + ">": "gt", + "=": "eq", +}; + +const unaryOperationToVmCmd: Record = { + "-": "neg", + "~": "not", +}; +/** + * Transforms parse tree into VM code + */ +export class VMWriter extends JackParserListener { + public result = ""; + private className = ""; + private currentLabelInd = 0; + private _localSymbolTable: LocalSymbolTable | undefined; + private afterEquals = false; + constructor(private globalSymbolTable: Record) { + super(); + } + + override enterClassDeclaration = (ctx: ClassDeclarationContext) => { + if (this.className != "") { + throw new Error("Cannot change class name"); + } + if (ctx.localSymbolTable == undefined) { + throw new Error("Local symbol table not found in parse tree"); + } + if (ctx.className() == null) { + throw new Error("Class name not found in parse tree"); + } + this.className = ctx.className().getText(); + this._localSymbolTable = ctx.localSymbolTable; + }; + get localSymbolTable() { + if (this._localSymbolTable == null) + throw new Error("Local symbol table is not initialized"); + return this._localSymbolTable; + } + + override enterSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { + const name = ctx + .subroutineDecWithoutType() + .subroutineName() + .IDENTIFIER() + .getText(); + const symbol = this.globalSymbolTable[this.className + "." + name]; + if (symbol == undefined) { + throw new Error( + `Can't find subroutine ${name} in class ${this.className} in symbol table`, + ); + } + if (symbol.subroutineInfo == null) { + throw new Error( + `Subroutine info not found for subroutine ${name} in class ${this.className}`, + ); + } + this.result += `function ${this.className}.${name} ${symbol.subroutineInfo.localVarsCount}\n`; + + if (ctx.subroutineType().CONSTRUCTOR() != null) { + this.result += ` push constant ${this.localSymbolTable.fieldsCount()}\n`; + this.result += " call Memory.alloc 1\n"; + this.result += " pop pointer 0\n"; + } else if (ctx.subroutineType().METHOD() != null) { + this.result += " push argument 0\n"; + this.result += " pop pointer 0\n"; + } + + if (ctx.symbols == null) { + throw new Error("Subroutine symbols not found in parse tree"); + } + this.localSymbolTable.setSubroutineScope(ctx.symbols); + }; + override exitArrayAccess = (ctx: ArrayAccessContext) => { + const varName = ctx.varName().IDENTIFIER().getText(); + const symbol = this.localSymbolTable.lookup(varName); + if (symbol == undefined) { + throw new Error(`Can't find variable ${varName} in local symbol table`); + } + this.pushSymbolOntoStack(symbol); + this.result += ` add\n`; + if (this.afterEquals || ctx.parent instanceof ExpressionContext) { + this.result += ` pop pointer 1\n`; + this.result += ` push that 0\n`; + } + }; + override enterEquals = () => { + this.afterEquals = true; + }; + override exitStatement = () => { + this.afterEquals = false; + }; + override enterConstant = (ctx: ConstantContext) => { + if (ctx.INTEGER_LITERAL() != null) { + this.result += ` push constant ${ctx.INTEGER_LITERAL()?.getText()}\n`; + } else if (ctx.booleanLiteral() != null) { + if (ctx.booleanLiteral()?.FALSE() != null) { + this.result += ` push constant 0\n`; + } else if (ctx.booleanLiteral()?.TRUE()) { + this.result += ` push constant 1\n`; + this.result += ` neg\n`; + } else { + throw new Error("Unknown boolean literal"); + } + } else if (ctx.THIS_LITERAL() != null) { + this.result += ` push pointer 0\n`; + } else if (ctx.STRING_LITERAL() != null) { + const maybeStr = ctx + .STRING_LITERAL() + ?.getText() + //cutoff "" + .slice(1, -1); + const str = assertExists(maybeStr, "String literal cannot be null"); + this.result += ` push constant ${str.length}\n`; + this.result += ` call String.new 1\n`; + for (const char of str) { + this.result += ` push constant ${char.charCodeAt(0)}\n`; + this.result += " call String.appendChar 2\n"; + } + } else if (ctx.NULL_LITERAL() != null) { + this.result += ` push constant 0\n`; + } else { + throw new Error("Unknown constant type"); + } + }; + override exitExpression = (ctx: ExpressionContext) => { + if (ctx.varName() != null) { + const varNameCtx = assertExists( + ctx.varName(), + "Variable name cannot be null", + ); + const varName = varNameCtx.IDENTIFIER().getText(); + const symbol = this.localSymbolTable.lookup(varName); + if (symbol == undefined) { + throw new Error( + `Cannot find variable ${varName} in arguments or local variables`, + ); + } + this.pushSymbolOntoStack(symbol); + } else if (ctx.binaryOperator() != null) { + const binaryOp = assertExists( + ctx.binaryOperator(), + "Binary operator cannot be null", + ).getText(); + if (binaryOperationToVmCmd[binaryOp] == undefined) { + throw new Error(`Unknown binary operator ${binaryOp}`); + } + this.result += " " + binaryOperationToVmCmd[binaryOp] + "\n"; + } else if (ctx.unaryOperation() != null) { + const unaryOp = assertExists( + ctx.unaryOperation()?.unaryOperator(), + "Unary operation cannot be null", + ).getText(); + if (unaryOperationToVmCmd[unaryOp] == null) { + throw new Error(`Unknown unary operator ${unaryOp}`); + } + this.result += " " + unaryOperationToVmCmd[unaryOp] + "\n"; + } + }; + pushSymbolOntoStack(symbol: VariableSymbol) { + this.result += ` push ${scopeTypeToString(symbol.scope)} ${symbol.index}\n`; + } + override exitLetStatement = (ctx: LetStatementContext) => { + if (ctx.varName() != null) { + const varNameCtx = assertExists(ctx.varName(), "Var name cannot be null"); + const symbol = this.localSymbolTable.lookup( + varNameCtx.IDENTIFIER().getText(), + ); + if (symbol == undefined) { + throw new Error( + `Can't find variable ${ctx.varName()?.IDENTIFIER().getText()} in local symbol table`, + ); + } + this.result += ` pop ${scopeTypeToString(symbol.scope)} ${symbol.index}\n`; + } else if (ctx.arrayAccess() != null) { + this.result += "pop temp 0\n"; + this.result += "pop pointer 1\n"; + this.result += "push temp 0\n"; + this.result += "pop that 0\n"; + } else { + throw new Error(`Unknown let statement type`); + } + }; + //if else + override enterIfStatement = (ctx: IfStatementContext) => { + ctx.endLabel = this.createLabel(); + }; + override exitIfStatement = (ctx: IfStatementContext) => { + const parent = ctx.parent as IfElseStatementContext; + if (parent.elseStatement() != null) { + parent.endLabel = this.createLabel(); + this.result += ` goto ${parent.endLabel}\n`; + } + this.result += `label ${ctx.endLabel} \n`; + }; + override exitIfExpression = (ctx: IfExpressionContext) => { + const parent = ctx.parent as IfStatementContext; + this.ifNotGoto(parent.endLabel); + }; + override exitIfElseStatement = (ctx: IfElseStatementContext) => { + if (ctx.endLabel) { + this.result += `label ${ctx.endLabel}\n`; + } + }; + //while + override enterWhileStatement = (ctx: WhileStatementContext) => { + ctx.startLabel = this.createLabel(); + ctx.endLabel = this.createLabel(); + this.result += `label ${ctx.startLabel}\n`; + }; + override exitWhileExpression = (ctx: WhileExpressionContext) => { + const parent = ctx.parent as WhileStatementContext; + this.ifNotGoto(parent.endLabel); + }; + + override exitWhileStatement = (ctx: WhileStatementContext) => { + this.result += ` goto ${ctx.startLabel}\n`; + this.result += `label ${ctx.endLabel}\n`; + }; + + override enterSubroutineCall = (ctx: SubroutineCallContext) => { + const { callType, symbol } = getCallType( + ctx.subroutineId(), + this.className, + this.localSymbolTable, + ); + if (callType === CallType.VarMethod) { + if (symbol == null) + throw new Error("Symbol not found when calling a method"); + this.pushSymbolOntoStack(symbol); + } else if (callType === CallType.LocalMethod) { + this.result += ` push pointer 0\n`; + } + }; + //do + override exitSubroutineCall = (ctx: SubroutineCallContext) => { + //method call + const { callType, subroutineIdText } = getCallType( + ctx.subroutineId(), + this.className, + this.localSymbolTable, + ); + switch (callType) { + case CallType.ClassFunctionOrConstructor: { + const argsCount = ctx.expressionList().expression().length; + this.result += ` call ${ctx.subroutineId().getText()} ${argsCount}\n`; + break; + } + case CallType.LocalMethod: + case CallType.VarMethod: { + const expressionsCount = ctx.expressionList().expression().length; + this.result += ` call ${subroutineIdText} ${expressionsCount + 1}\n`; + break; + } + default: + throw new Error(`Unknown call type ${callType}`); + } + }; + override exitDoStatement = (ctx: DoStatementContext) => { + this.result += ` pop temp 0\n`; + }; + //return + override exitReturnStatement = (ctx: ReturnStatementContext) => { + if (ctx.expression() == null) { + this.result += " push constant 0\n"; + } + this.result += " return\n"; + }; + //Utils + ifNotGoto(endLabel: string) { + this.result += " not\n"; + this.result += ` if-goto ${endLabel}\n`; + } + getLabel(ind: number) { + return `${this.className}_${ind} `; + } + + createLabel() { + return this.getLabel(this.currentLabelInd++); + } +} diff --git a/simulator/src/jack/parser.test.ts b/simulator/src/jack/parser.test.ts new file mode 100644 index 000000000..0ef7b328e --- /dev/null +++ b/simulator/src/jack/parser.test.ts @@ -0,0 +1,63 @@ +import fs from "fs"; + +import path from "path"; +import { + getTestResourcePath, + listenToTheTree, + parseJackFile, + parseJackText, + testResourceDirs, +} from "./test.helper"; +import { GlobalSymbolTableListener } from "./listener/global.symbol.listener"; + +describe("Jack parser", () => { + const jestConsole = console; + beforeEach(() => { + global.console = require("console"); + }); + + afterEach(() => { + global.console = jestConsole; + }); + + test.each(testResourceDirs)("%s", (dir: string) => { + testJackDir(getTestResourcePath(dir)); + }); + test("expected EOF", () => { + try { + parseJackText(` + class A{ + } + var a; + `); + } catch (e) { + if (e instanceof Error) { + expect(e.message).toContain("expecting "); + return; + } else { + fail("Expected Error"); + } + } + fail("Expected Error"); + }); +}); + +function testJackDir(testFolder: string): void { + const files = fs + .readdirSync(testFolder) + .filter((file) => file.endsWith(".jack")) + .map((file) => path.join(testFolder, file)); + for (const filePath of files) { + const tree = parseJackFile(filePath); + const globalSymbolsListener = listenToTheTree( + tree, + new GlobalSymbolTableListener(), + ); + const symbolsErrors = globalSymbolsListener.errors.join("\n"); + try { + expect(globalSymbolsListener.errors.length).toBe(0); + } catch (e) { + throw new Error(symbolsErrors); + } + } +} diff --git a/simulator/src/jack/resources/test/Average/Main.jack b/simulator/src/jack/resources/test/Average/Main.jack new file mode 100644 index 000000000..d53093a22 --- /dev/null +++ b/simulator/src/jack/resources/test/Average/Main.jack @@ -0,0 +1,27 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Average/Main.jack + +// Inputs some numbers and computes their average +class Main { + function void main() { + var Array a; + var int length; + var int i, sum; + + let length = Keyboard.readInt("How many numbers? "); + let a = Array.new(length); // constructs the array + + let i = 0; + while (i < length) { + let a[i] = Keyboard.readInt("Enter a number: "); + let sum = sum + a[i]; + let i = i + 1; + } + + do Output.printString("The average is "); + do Output.printInt(sum / length); + return; + } +} diff --git a/simulator/src/jack/resources/test/Average/Main.vm b/simulator/src/jack/resources/test/Average/Main.vm new file mode 100644 index 000000000..bd88b53a7 --- /dev/null +++ b/simulator/src/jack/resources/test/Average/Main.vm @@ -0,0 +1,150 @@ +function Main.main 4 + push constant 18 + call String.new 1 + push constant 72 + call String.appendChar 2 + push constant 111 + call String.appendChar 2 + push constant 119 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 109 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 110 + call String.appendChar 2 + push constant 121 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 110 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 109 + call String.appendChar 2 + push constant 98 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 63 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Keyboard.readInt 1 + pop local 1 + push local 1 + call Array.new 1 + pop local 0 + push constant 0 + pop local 2 +label Main_0 + push local 2 + push local 1 + lt + not + if-goto Main_1 + push local 2 + push local 0 + add + push constant 16 + call String.new 1 + push constant 69 + call String.appendChar 2 + push constant 110 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 110 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 109 + call String.appendChar 2 + push constant 98 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Keyboard.readInt 1 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push local 3 + push local 2 + push local 0 + add + pop pointer 1 + push that 0 + add + pop local 3 + push local 2 + push constant 1 + add + pop local 2 + goto Main_0 + +label Main_1 + push constant 15 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 104 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 118 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 103 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 105 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push local 3 + push local 1 + call Math.divide 2 + call Output.printInt 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/ComplexArrays/Main.jack b/simulator/src/jack/resources/test/ComplexArrays/Main.jack new file mode 100644 index 000000000..b4d7dca2e --- /dev/null +++ b/simulator/src/jack/resources/test/ComplexArrays/Main.jack @@ -0,0 +1,69 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/ComplexArrays/Main.jack +/** + * Performs several complex array processing tests. + * For each test, the expected result is printed, along with the + * actual result. In each test, the two results should be equal. + */ +class Main { + + function void main() { + var Array a, b, c; + + let a = Array.new(10); + let b = Array.new(5); + let c = Array.new(1); + + let a[3] = 2; + let a[4] = 8; + let a[5] = 4; + let b[a[3]] = a[3] + 3; // b[2] = 5 + let a[b[a[3]]] = a[a[5]] * b[((7 - a[3]) - Main.double(2)) + 1]; // a[5] = 8 * 5 = 40 + let c[0] = null; + let c = c[0]; + + do Output.printString("Test 1: expected result: 5; actual result: "); + do Output.printInt(b[2]); + do Output.println(); + do Output.printString("Test 2: expected result: 40; actual result: "); + do Output.printInt(a[5]); + do Output.println(); + do Output.printString("Test 3: expected result: 0; actual result: "); + do Output.printInt(c); + do Output.println(); + + let c = null; + + if (c = null) { + do Main.fill(a, 10); + let c = a[3]; + let c[1] = 33; + let c = a[7]; + let c[1] = 77; + let b = a[3]; + let b[1] = b[1] + c[1]; // b[1] = 33 + 77 = 110; + } + + do Output.printString("Test 4: expected result: 77; actual result: "); + do Output.printInt(c[1]); + do Output.println(); + do Output.printString("Test 5: expected result: 110; actual result: "); + do Output.printInt(b[1]); + do Output.println(); + return; + } + + function int double(int a) { + return a * 2; + } + + function void fill(Array a, int size) { + while (size > 0) { + let size = size - 1; + let a[size] = Array.new(3); + } + return; + } +} diff --git a/simulator/src/jack/resources/test/ComplexArrays/Main.vm b/simulator/src/jack/resources/test/ComplexArrays/Main.vm new file mode 100644 index 000000000..d125bde9e --- /dev/null +++ b/simulator/src/jack/resources/test/ComplexArrays/Main.vm @@ -0,0 +1,704 @@ +function Main.main 3 + push constant 10 + call Array.new 1 + pop local 0 + push constant 5 + call Array.new 1 + pop local 1 + push constant 1 + call Array.new 1 + pop local 2 + push constant 3 + push local 0 + add + push constant 2 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 4 + push local 0 + add + push constant 8 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 5 + push local 0 + add + push constant 4 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + push local 1 + add + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + push constant 3 + add + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + push local 1 + add + pop pointer 1 + push that 0 + push local 0 + add + push constant 5 + push local 0 + add + pop pointer 1 + push that 0 + push local 0 + add + pop pointer 1 + push that 0 + push constant 7 + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + sub + push constant 2 + call Main.double 1 + sub + push constant 1 + add + push local 1 + add + pop pointer 1 + push that 0 + call Math.multiply 2 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 0 + push local 2 + add + push constant 0 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 0 + push local 2 + add + pop pointer 1 + push that 0 + pop local 2 + push constant 43 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 49 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 120 + call String.appendChar 2 + push constant 112 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 53 + call String.appendChar 2 + push constant 59 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push constant 2 + push local 1 + add + pop pointer 1 + push that 0 + call Output.printInt 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 44 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 50 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 120 + call String.appendChar 2 + push constant 112 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 52 + call String.appendChar 2 + push constant 48 + call String.appendChar 2 + push constant 59 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push constant 5 + push local 0 + add + pop pointer 1 + push that 0 + call Output.printInt 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 43 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 51 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 120 + call String.appendChar 2 + push constant 112 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 48 + call String.appendChar 2 + push constant 59 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push local 2 + call Output.printInt 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 0 + pop local 2 + push local 2 + push constant 0 + eq + not + if-goto Main_0 + push local 0 + push constant 10 + call Main.fill 2 + pop temp 0 + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + pop local 2 + push constant 1 + push local 2 + add + push constant 33 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 7 + push local 0 + add + pop pointer 1 + push that 0 + pop local 2 + push constant 1 + push local 2 + add + push constant 77 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + push constant 3 + push local 0 + add + pop pointer 1 + push that 0 + pop local 1 + push constant 1 + push local 1 + add + push constant 1 + push local 1 + add + pop pointer 1 + push that 0 + push constant 1 + push local 2 + add + pop pointer 1 + push that 0 + add + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 +label Main_0 + push constant 44 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 52 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 120 + call String.appendChar 2 + push constant 112 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 55 + call String.appendChar 2 + push constant 55 + call String.appendChar 2 + push constant 59 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push constant 1 + push local 2 + add + pop pointer 1 + push that 0 + call Output.printInt 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 45 + call String.new 1 + push constant 84 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 53 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 120 + call String.appendChar 2 + push constant 112 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 49 + call String.appendChar 2 + push constant 49 + call String.appendChar 2 + push constant 48 + call String.appendChar 2 + push constant 59 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 115 + call String.appendChar 2 + push constant 117 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 116 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push constant 1 + push local 1 + add + pop pointer 1 + push that 0 + call Output.printInt 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 0 + return +function Main.double 0 + push argument 0 + push constant 2 + call Math.multiply 2 + return +function Main.fill 0 +label Main_1 + //size>0 + push argument 1 + push constant 0 + gt + not + if-goto Main_2 + //let size = size-1 + push argument 1 + push constant 1 + sub + pop argument 1 + //let a[size] = Array.new(3); + push argument 1 + push argument 0 + add + push constant 3 + call Array.new 1 + pop temp 0 + pop pointer 1 + push temp 0 + pop that 0 + goto Main_1 +label Main_2 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/ConvertToBin/Main.jack b/simulator/src/jack/resources/test/ConvertToBin/Main.jack new file mode 100644 index 000000000..12ce3ce60 --- /dev/null +++ b/simulator/src/jack/resources/test/ConvertToBin/Main.jack @@ -0,0 +1,79 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/ConvertToBin/Main.jack +/** + * Unpacks a 16-bit number into its binary representation: + * Takes the 16-bit number stored in RAM[8000] and stores its individual + * bits in RAM[8001]..RAM[8016] (each location will contain 0 or 1). + * Before the conversion, RAM[8001]..RAM[8016] are initialized to -1. + * + * The program should be tested as follows: + * 1) Load the compiled program into the supplied VM emulator + * 2) Put some value in RAM[8000] + * 3) Switch to "no animation" + * 4) Run the program (give it enough time to run) + * 5) Stop the program + * 6) Check that RAM[8001]..RAM[8016] contains the correct binary result, and + * that none of these memory locations contains -1. + */ +class Main { + /** Initializes RAM[8001]..RAM[8016] to -1, + * and converts the value in RAM[8000] to binary. */ + function void main() { + var int value; + do Main.fillMemory(8001, 16, -1); // sets RAM[8001]..RAM[8016] to -1 + let value = Memory.peek(8000); // reads a value from RAM[8000] + do Main.convert(value); // performs the conversion + return; + } + + /** Converts the given decimal value to binary, and puts + * the resulting bits in RAM[8001]..RAM[8016]. */ + function void convert(int value) { + var int mask, position; + var boolean loop; + + let loop = true; + while (loop) { + let position = position + 1; + let mask = Main.nextMask(mask); + + if (~(position > 16)) { + + if (~((value & mask) = 0)) { + do Memory.poke(8000 + position, 1); + } + else { + do Memory.poke(8000 + position, 0); + } + } + else { + let loop = false; + } + } + return; + } + + /** Returns the next mask (the mask that should follow the given mask). */ + function int nextMask(int mask) { + if (mask = 0) { + return 1; + } + else { + return mask * 2; + } + } + + /** Fills 'length' consecutive memory locations with 'value', + * starting at 'startAddress'. */ + function void fillMemory(int startAddress, int length, int value) { + while (length > 0) { + do Memory.poke(startAddress, value); + let length = length - 1; + let startAddress = startAddress + 1; + } + return; + } + +} diff --git a/simulator/src/jack/resources/test/ConvertToBin/Main.vm b/simulator/src/jack/resources/test/ConvertToBin/Main.vm new file mode 100644 index 000000000..f83fa9291 --- /dev/null +++ b/simulator/src/jack/resources/test/ConvertToBin/Main.vm @@ -0,0 +1,106 @@ +function Main.main 1 + push constant 8001 + push constant 16 + push constant 1 + neg + call Main.fillMemory 3 + pop temp 0 + push constant 8000 + call Memory.peek 1 + pop local 0 + push local 0 + call Main.convert 1 + pop temp 0 + push constant 0 + return +function Main.convert 3 + push constant 1 + neg + pop local 2 +label Main_0 + push local 2 + not + if-goto Main_1 + push local 1 + push constant 1 + add + pop local 1 + push local 0 + call Main.nextMask 1 + pop local 0 + push local 1 + push constant 16 + gt + not + not + if-goto Main_2 + push argument 0 + push local 0 + and + push constant 0 + eq + not + not + if-goto Main_3 + push constant 8000 + push local 1 + add + push constant 1 + call Memory.poke 2 + pop temp 0 + goto Main_4 +label Main_3 + push constant 8000 + push local 1 + add + push constant 0 + call Memory.poke 2 + pop temp 0 +label Main_4 + goto Main_5 +label Main_2 + push constant 0 + pop local 2 +label Main_5 + goto Main_0 +label Main_1 + push constant 0 + return +function Main.nextMask 0 + push argument 0 + push constant 0 + eq + not + if-goto Main_6 + push constant 1 + return + goto Main_7 +label Main_6 + push argument 0 + push constant 2 + call Math.multiply 2 + return +label Main_7 +function Main.fillMemory 0 +label Main_8 + push argument 1 + push constant 0 + gt + not + if-goto Main_9 + push argument 0 + push argument 2 + call Memory.poke 2 + pop temp 0 + push argument 1 + push constant 1 + sub + pop argument 1 + push argument 0 + push constant 1 + add + pop argument 0 + goto Main_8 +label Main_9 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Fraction/Fraction.jack b/simulator/src/jack/resources/test/Fraction/Fraction.jack new file mode 100644 index 000000000..939df83f5 --- /dev/null +++ b/simulator/src/jack/resources/test/Fraction/Fraction.jack @@ -0,0 +1,65 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Fraction/Fraction.jack + +/** Represents the Fraction type and related operations. */ +class Fraction { + field int numerator, denominator; // field = property = member variable. + + /** Constructs a (reduced) fraction from the given numerator and denominator. */ + constructor Fraction new(int x, int y) { + let numerator = x; + let denominator = y; + do reduce(); // reduces the fraction + return this; // a constructor is expected to return a reference to the new object + } + + // Reduces this fraction. + method void reduce() { + var int g; + let g = Fraction.gcd(numerator, denominator); + if (g > 1) { + let numerator = numerator / g; + let denominator = denominator / g; + } + return; + } + + /** Accessors. */ + method int getNumerator() { return numerator; } + method int getDenominator() { return denominator; } + + /** Returns the sum of this fraction and the other one. */ + method Fraction plus(Fraction other) { + var int sum; + let sum = (numerator * other.getDenominator()) + (other.getNumerator() * denominator); + return Fraction.new(sum, denominator * other.getDenominator()); + } + + // More fraction-related methods (minus, times, div, invert, etc.) can be added here. + + /** Disposes this fraction. */ + method void dispose() { + do Memory.deAlloc(this); // uses an OS routine to recycle the memory held by the object + return; + } + + /** Prints this fraction in the format x/y. */ + method void print() { + do Output.printInt(numerator); + do Output.printString("/"); + do Output.printInt(denominator); + return; + } + + // Computes the greatest common divisor of the given integers. + function int gcd(int a, int b) { + var int r; + while (~(b = 0)) { // applies Euclid's algorithm + let r = a - (b * (a / b)); // r = remainder of the integer division a/b + let a = b; let b = r; + } + return a; + } +} diff --git a/simulator/src/jack/resources/test/Fraction/Fraction.vm b/simulator/src/jack/resources/test/Fraction/Fraction.vm new file mode 100644 index 000000000..1f5e205ad --- /dev/null +++ b/simulator/src/jack/resources/test/Fraction/Fraction.vm @@ -0,0 +1,115 @@ +function Fraction.new 0 + push constant 2 + call Memory.alloc 1 + pop pointer 0 + push argument 0 + pop this 0 + push argument 1 + pop this 1 + push pointer 0 + call Fraction.reduce 1 + pop temp 0 + push pointer 0 + return +function Fraction.reduce 1 + push argument 0 + pop pointer 0 + push this 0 + push this 1 + call Fraction.gcd 2 + pop local 0 + push local 0 + push constant 1 + gt + not + if-goto Fraction_0 + push this 0 + push local 0 + call Math.divide 2 + pop this 0 + push this 1 + push local 0 + call Math.divide 2 + pop this 1 +label Fraction_0 + push constant 0 + return +function Fraction.getNumerator 0 + push argument 0 + pop pointer 0 + push this 0 + return +function Fraction.getDenominator 0 + push argument 0 + pop pointer 0 + push this 1 + return +function Fraction.plus 1 + push argument 0 + pop pointer 0 + push this 0 + push argument 1 + call Fraction.getDenominator 1 + call Math.multiply 2 + push argument 1 + call Fraction.getNumerator 1 + push this 1 + call Math.multiply 2 + add + pop local 0 + push local 0 + push this 1 + push argument 1 + call Fraction.getDenominator 1 + call Math.multiply 2 + call Fraction.new 2 + return +function Fraction.dispose 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function Fraction.print 0 + push argument 0 + pop pointer 0 + push this 0 + call Output.printInt 1 + pop temp 0 + push constant 1 + call String.new 1 + push constant 47 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push this 1 + call Output.printInt 1 + pop temp 0 + push constant 0 + return +function Fraction.gcd 1 +label Fraction_1 + push argument 1 + push constant 0 + eq + not + not + if-goto Fraction_2 + push argument 0 + push argument 1 + push argument 0 + push argument 1 + call Math.divide 2 + call Math.multiply 2 + sub + pop local 0 + push argument 1 + pop argument 0 + push local 0 + pop argument 1 + goto Fraction_1 +label Fraction_2 + push argument 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Fraction/Main.jack b/simulator/src/jack/resources/test/Fraction/Main.jack new file mode 100644 index 000000000..293bc9170 --- /dev/null +++ b/simulator/src/jack/resources/test/Fraction/Main.jack @@ -0,0 +1,16 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Fraction/Main.jack + +// Computes and prints the sum of 2/3 and 1/5. +class Main { + function void main() { + var Fraction a, b, c; + let a = Fraction.new(2,3); + let b = Fraction.new(1,5); + let c = a.plus(b); // Computes c = a + b + do c.print(); // Prints "13/15" + return; + } +} diff --git a/simulator/src/jack/resources/test/Fraction/Main.vm b/simulator/src/jack/resources/test/Fraction/Main.vm new file mode 100644 index 000000000..fcf795d39 --- /dev/null +++ b/simulator/src/jack/resources/test/Fraction/Main.vm @@ -0,0 +1,18 @@ +function Main.main 3 + push constant 2 + push constant 3 + call Fraction.new 2 + pop local 0 + push constant 1 + push constant 5 + call Fraction.new 2 + pop local 1 + push local 0 + push local 1 + call Fraction.plus 2 + pop local 2 + push local 2 + call Fraction.print 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/HelloWorld/Main.jack b/simulator/src/jack/resources/test/HelloWorld/Main.jack new file mode 100644 index 000000000..6cb47f470 --- /dev/null +++ b/simulator/src/jack/resources/test/HelloWorld/Main.jack @@ -0,0 +1,14 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/HelloWorld/Main.jack + +/** Hello World program. */ +class Main { + function void main() { + /* Prints some text using the standard library. */ + do Output.printString("Hello world!"); + do Output.println(); // New line + return; + } +} diff --git a/simulator/src/jack/resources/test/HelloWorld/Main.vm b/simulator/src/jack/resources/test/HelloWorld/Main.vm new file mode 100644 index 000000000..1a9240be5 --- /dev/null +++ b/simulator/src/jack/resources/test/HelloWorld/Main.vm @@ -0,0 +1,33 @@ +function Main.main 0 + push constant 12 + call String.new 1 + push constant 72 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 111 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 119 + call String.appendChar 2 + push constant 111 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 108 + call String.appendChar 2 + push constant 100 + call String.appendChar 2 + push constant 33 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + call Output.println 0 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/List/List.jack b/simulator/src/jack/resources/test/List/List.jack new file mode 100644 index 000000000..49bc54a87 --- /dev/null +++ b/simulator/src/jack/resources/test/List/List.jack @@ -0,0 +1,48 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/List/List.jack + +/** Represents a linked list of integers. */ +class List { + field int data; // an int value, + field List next; // followed by a list of int values + + /* Creates a List. */ + constructor List new(int car, List cdr) { + let data = car; // the identifiers car and cdr are used in + let next = cdr; // memory of the Lisp programming language + return this; + } + + /** Accessors. */ + method int getData() { return data; } + method int getNext() { return next; } + + /** Prints this list. */ + method void print() { + // Sets current to the first element of this list + var List current; + let current = this; + while (~(current = null)) { + do Output.printInt(current.getData()); + do Output.printChar(32); // prints a space + let current = current.getNext(); + } + return; + } + + /** Disposes this List. */ + // By recursively disposing its tail. + method void dispose() { + if (~(next = null)) { + do next.dispose(); + } + // Calls an OS routine to free the memory of this object. + do Memory.deAlloc(this); + return; + } + + // More list processing methods can come here. + +} diff --git a/simulator/src/jack/resources/test/List/List.vm b/simulator/src/jack/resources/test/List/List.vm new file mode 100644 index 000000000..71f197ed0 --- /dev/null +++ b/simulator/src/jack/resources/test/List/List.vm @@ -0,0 +1,64 @@ +function List.new 0 + push constant 2 + call Memory.alloc 1 + pop pointer 0 + push argument 0 + pop this 0 + push argument 1 + pop this 1 + push pointer 0 + return +function List.getData 0 + push argument 0 + pop pointer 0 + push this 0 + return +function List.getNext 0 + push argument 0 + pop pointer 0 + push this 1 + return +function List.print 1 + push argument 0 + pop pointer 0 + push pointer 0 + pop local 0 +label List_0 + push local 0 + push constant 0 + eq + not + not + if-goto List_1 + push local 0 + call List.getData 1 + call Output.printInt 1 + pop temp 0 + push constant 32 + call Output.printChar 1 + pop temp 0 + push local 0 + call List.getNext 1 + pop local 0 + goto List_0 +label List_1 + push constant 0 + return +function List.dispose 0 + push argument 0 + pop pointer 0 + push this 1 + push constant 0 + eq + not + not + if-goto List_2 + push this 1 + call List.dispose 1 + pop temp 0 +label List_2 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/List/Main.jack b/simulator/src/jack/resources/test/List/Main.jack new file mode 100644 index 000000000..54a39a975 --- /dev/null +++ b/simulator/src/jack/resources/test/List/Main.jack @@ -0,0 +1,17 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/List/Main.jack + +/** An example of creating and using List objects. */ +class Main { + function void main() { + // Creates and uses the list (2,3,5). + var List v; + let v = List.new(5,null); + let v = List.new(2,List.new(3,v)); + do v.print(); // prints 2 3 5 + do v.dispose(); // disposes the list + return; + } +} diff --git a/simulator/src/jack/resources/test/List/Main.vm b/simulator/src/jack/resources/test/List/Main.vm new file mode 100644 index 000000000..171cfd914 --- /dev/null +++ b/simulator/src/jack/resources/test/List/Main.vm @@ -0,0 +1,19 @@ +function Main.main 1 + push constant 5 + push constant 0 + call List.new 2 + pop local 0 + push constant 2 + push constant 3 + push local 0 + call List.new 2 + call List.new 2 + pop local 0 + push local 0 + call List.print 1 + pop temp 0 + push local 0 + call List.dispose 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Pong/Ball.jack b/simulator/src/jack/resources/test/Pong/Ball.jack new file mode 100644 index 000000000..f65a90753 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Ball.jack @@ -0,0 +1,202 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Pong/Ball.jack +/** + * A graphical ball in a Pong game. Characterized by a screen location and + * distance of last destination. Has methods for drawing, erasing and moving + * on the screen. The ball is displayed as a filled, 6-by-6 pixles rectangle. + */ +class Ball { + + field int x, y; // the ball's screen location (in pixels) + field int lengthx, lengthy; // distance of last destination (in pixels) + + field int d, straightD, diagonalD; // used for straight line movement computation + field boolean invert, positivex, positivey; // (same) + + field int leftWall, rightWall, topWall, bottomWall; // wall locations + + field int wall; // last wall that the ball was bounced off of + + /** Constructs a new ball with the given initial location and wall locations. */ + constructor Ball new(int Ax, int Ay, + int AleftWall, int ArightWall, int AtopWall, int AbottomWall) { + let x = Ax; + let y = Ay; + let leftWall = AleftWall; + let rightWall = ArightWall - 6; // -6 for ball size + let topWall = AtopWall; + let bottomWall = AbottomWall - 6; // -6 for ball size + let wall = 0; + do show(); + return this; + } + + /** Deallocates the Ball's memory. */ + method void dispose() { + do Memory.deAlloc(this); + return; + } + + /** Shows the ball. */ + method void show() { + do Screen.setColor(true); + do draw(); + return; + } + + /** Hides the ball. */ + method void hide() { + do Screen.setColor(false); + do draw(); + return; + } + + /** Draws the ball. */ + method void draw() { + do Screen.drawRectangle(x, y, x + 5, y + 5); + return; + } + + /** Returns the ball's left edge. */ + method int getLeft() { + return x; + } + + /** Returns the ball's right edge. */ + method int getRight() { + return x + 5; + } + + /** Computes and sets the ball's destination. */ + method void setDestination(int destx, int desty) { + var int dx, dy, temp; + let lengthx = destx - x; + let lengthy = desty - y; + let dx = Math.abs(lengthx); + let dy = Math.abs(lengthy); + let invert = (dx < dy); + + if (invert) { + let temp = dx; // swap dx, dy + let dx = dy; + let dy = temp; + let positivex = (y < desty); + let positivey = (x < destx); + } + else { + let positivex = (x < destx); + let positivey = (y < desty); + } + + let d = (2 * dy) - dx; + let straightD = 2 * dy; + let diagonalD = 2 * (dy - dx); + + return; + } + + /** + * Moves the ball one step towards its destination. + * If the ball has reached a wall, returns 0. + * Else, returns a value according to the wall: + * 1 (left wall), 2 (right wall), 3 (top wall), 4 (bottom wall). + */ + method int move() { + + do hide(); + + if (d < 0) { let d = d + straightD; } + else { + let d = d + diagonalD; + + if (positivey) { + if (invert) { let x = x + 4; } + else { let y = y + 4; } + } + else { + if (invert) { let x = x - 4; } + else { let y = y - 4; } + } + } + + if (positivex) { + if (invert) { let y = y + 4; } + else { let x = x + 4; } + } + else { + if (invert) { let y = y - 4; } + else { let x = x - 4; } + } + + if (~(x > leftWall)) { + let wall = 1; + let x = leftWall; + } + if (~(x < rightWall)) { + let wall = 2; + let x = rightWall; + } + if (~(y > topWall)) { + let wall = 3; + let y = topWall; + } + if (~(y < bottomWall)) { + let wall = 4; + let y = bottomWall; + } + + do show(); + + return wall; + } + + /** + * Bounces off the current wall: sets the new destination + * of the ball according to the ball's angle and the given + * bouncing direction (-1/0/1=left/center/right or up/center/down). + */ + method void bounce(int bouncingDirection) { + var int newx, newy, divLengthx, divLengthy, factor; + + // Since results are too big, divides by 10 + let divLengthx = lengthx / 10; + let divLengthy = lengthy / 10; + if (bouncingDirection = 0) { let factor = 10; } + else { + if (((~(lengthx < 0)) & (bouncingDirection = 1)) | ((lengthx < 0) & (bouncingDirection = (-1)))) { + let factor = 20; // bounce direction is in ball direction + } + else { let factor = 5; } // bounce direction is against ball direction + } + + if (wall = 1) { + let newx = 506; + let newy = (divLengthy * (-50)) / divLengthx; + let newy = y + (newy * factor); + } + else { + if (wall = 2) { + let newx = 0; + let newy = (divLengthy * 50) / divLengthx; + let newy = y + (newy * factor); + } + else { + if (wall = 3) { + let newy = 250; + let newx = (divLengthx * (-25)) / divLengthy; + let newx = x + (newx * factor); + } + else { // assumes wall = 4 + let newy = 0; + let newx = (divLengthx * 25) / divLengthy; + let newx = x + (newx * factor); + } + } + } + + do setDestination(newx, newy); + return; + } +} diff --git a/simulator/src/jack/resources/test/Pong/Ball.vm b/simulator/src/jack/resources/test/Pong/Ball.vm new file mode 100644 index 000000000..fe2928826 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Ball.vm @@ -0,0 +1,427 @@ +function Ball.new 0 + push constant 15 + call Memory.alloc 1 + pop pointer 0 + push argument 0 + pop this 0 + push argument 1 + pop this 1 + push argument 2 + pop this 10 + push argument 3 + push constant 6 + sub + pop this 11 + push argument 4 + pop this 12 + push argument 5 + push constant 6 + sub + pop this 13 + push constant 0 + pop this 14 + push pointer 0 + call Ball.show 1 + pop temp 0 + push pointer 0 + return +function Ball.dispose 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function Ball.show 0 + push argument 0 + pop pointer 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push pointer 0 + call Ball.draw 1 + pop temp 0 + push constant 0 + return +function Ball.hide 0 + push argument 0 + pop pointer 0 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push pointer 0 + call Ball.draw 1 + pop temp 0 + push constant 0 + return +function Ball.draw 0 + push argument 0 + pop pointer 0 + push this 0 + push this 1 + push this 0 + push constant 5 + add + push this 1 + push constant 5 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 0 + return +function Ball.getLeft 0 + push argument 0 + pop pointer 0 + push this 0 + return +function Ball.getRight 0 + push argument 0 + pop pointer 0 + push this 0 + push constant 5 + add + return +function Ball.setDestination 3 + push argument 0 + pop pointer 0 + push argument 1 + push this 0 + sub + pop this 2 + push argument 2 + push this 1 + sub + pop this 3 + push this 2 + call Math.abs 1 + pop local 0 + push this 3 + call Math.abs 1 + pop local 1 + push local 0 + push local 1 + lt + pop this 7 + push this 7 + not + if-goto Ball_0 + push local 0 + pop local 2 + push local 1 + pop local 0 + push local 2 + pop local 1 + push this 1 + push argument 2 + lt + pop this 8 + push this 0 + push argument 1 + lt + pop this 9 + goto Ball_1 +label Ball_0 + push this 0 + push argument 1 + lt + pop this 8 + push this 1 + push argument 2 + lt + pop this 9 +label Ball_1 + push constant 2 + push local 1 + call Math.multiply 2 + push local 0 + sub + pop this 4 + push constant 2 + push local 1 + call Math.multiply 2 + pop this 5 + push constant 2 + push local 1 + push local 0 + sub + call Math.multiply 2 + pop this 6 + push constant 0 + return +function Ball.move 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Ball.hide 1 + pop temp 0 + push this 4 + push constant 0 + lt + not + if-goto Ball_2 + push this 4 + push this 5 + add + pop this 4 + goto Ball_3 +label Ball_2 + push this 4 + push this 6 + add + pop this 4 + push this 9 + not + if-goto Ball_4 + push this 7 + not + if-goto Ball_5 + push this 0 + push constant 4 + add + pop this 0 + goto Ball_6 +label Ball_5 + push this 1 + push constant 4 + add + pop this 1 +label Ball_6 + goto Ball_7 +label Ball_4 + push this 7 + not + if-goto Ball_8 + push this 0 + push constant 4 + sub + pop this 0 + goto Ball_9 +label Ball_8 + push this 1 + push constant 4 + sub + pop this 1 +label Ball_9 +label Ball_7 +label Ball_3 + push this 8 + not + if-goto Ball_10 + push this 7 + not + if-goto Ball_11 + push this 1 + push constant 4 + add + pop this 1 + goto Ball_12 +label Ball_11 + push this 0 + push constant 4 + add + pop this 0 +label Ball_12 + goto Ball_13 +label Ball_10 + push this 7 + not + if-goto Ball_14 + push this 1 + push constant 4 + sub + pop this 1 + goto Ball_15 +label Ball_14 + push this 0 + push constant 4 + sub + pop this 0 +label Ball_15 +label Ball_13 + push this 0 + push this 10 + gt + not + not + if-goto Ball_16 + push constant 1 + pop this 14 + push this 10 + pop this 0 +label Ball_16 + push this 0 + push this 11 + lt + not + not + if-goto Ball_17 + push constant 2 + pop this 14 + push this 11 + pop this 0 +label Ball_17 + push this 1 + push this 12 + gt + not + not + if-goto Ball_18 + push constant 3 + pop this 14 + push this 12 + pop this 1 +label Ball_18 + push this 1 + push this 13 + lt + not + not + if-goto Ball_19 + push constant 4 + pop this 14 + push this 13 + pop this 1 +label Ball_19 + push pointer 0 + call Ball.show 1 + pop temp 0 + push this 14 + return +function Ball.bounce 5 + push argument 0 + pop pointer 0 + push this 2 + push constant 10 + call Math.divide 2 + pop local 2 + push this 3 + push constant 10 + call Math.divide 2 + pop local 3 + push argument 1 + push constant 0 + eq + not + if-goto Ball_20 + push constant 10 + pop local 4 + goto Ball_21 +label Ball_20 + push this 2 + push constant 0 + lt + not + push argument 1 + push constant 1 + eq + and + push this 2 + push constant 0 + lt + push argument 1 + push constant 1 + neg + eq + and + or + not + if-goto Ball_22 + push constant 20 + pop local 4 + goto Ball_23 +label Ball_22 + push constant 5 + pop local 4 +label Ball_23 +label Ball_21 + push this 14 + push constant 1 + eq + not + if-goto Ball_24 + push constant 506 + pop local 0 + push local 3 + push constant 50 + neg + call Math.multiply 2 + push local 2 + call Math.divide 2 + pop local 1 + push this 1 + push local 1 + push local 4 + call Math.multiply 2 + add + pop local 1 + goto Ball_25 +label Ball_24 + push this 14 + push constant 2 + eq + not + if-goto Ball_26 + push constant 0 + pop local 0 + push local 3 + push constant 50 + call Math.multiply 2 + push local 2 + call Math.divide 2 + pop local 1 + push this 1 + push local 1 + push local 4 + call Math.multiply 2 + add + pop local 1 + goto Ball_27 +label Ball_26 + push this 14 + push constant 3 + eq + not + if-goto Ball_28 + push constant 250 + pop local 1 + push local 2 + push constant 25 + neg + call Math.multiply 2 + push local 3 + call Math.divide 2 + pop local 0 + push this 0 + push local 0 + push local 4 + call Math.multiply 2 + add + pop local 0 + goto Ball_29 +label Ball_28 + push constant 0 + pop local 1 + push local 2 + push constant 25 + call Math.multiply 2 + push local 3 + call Math.divide 2 + pop local 0 + push this 0 + push local 0 + push local 4 + call Math.multiply 2 + add + pop local 0 +label Ball_29 +label Ball_27 +label Ball_25 + push pointer 0 + push local 0 + push local 1 + call Ball.setDestination 3 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Pong/Bat.jack b/simulator/src/jack/resources/test/Pong/Bat.jack new file mode 100644 index 000000000..b449c2c58 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Bat.jack @@ -0,0 +1,103 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Pong/Bat.jack +/** + * A graphical bat in a Pong game. + * Displayed as a filled horizontal rectangle that has a screen location, + * a width and a height. + * Has methods for drawing, erasing, moving left and right, and changing + * its width (to make the hitting action more challenging). + * This class should have been called "Paddle", following the + * standard Pong terminology. Unaware of this terminology, + * we called it "bat", and the name stuck. + */ +class Bat { + + field int x, y; // the bat's screen location + field int width, height; // the bat's width and height + field int direction; // direction of the bat's movement + // (1 = left, 2 = right) + + /** Constructs a new bat with the given location and width. */ + constructor Bat new(int Ax, int Ay, int Awidth, int Aheight) { + let x = Ax; + let y = Ay; + let width = Awidth; + let height = Aheight; + let direction = 2; + do show(); + return this; + } + + /** Deallocates the object's memory. */ + method void dispose() { + do Memory.deAlloc(this); + return; + } + + /** Shows the bat. */ + method void show() { + do Screen.setColor(true); + do draw(); + return; + } + + /** Hides the bat. */ + method void hide() { + do Screen.setColor(false); + do draw(); + return; + } + + /** Draws the bat. */ + method void draw() { + do Screen.drawRectangle(x, y, x + width, y + height); + return; + } + + /** Sets the bat's direction (0=stop, 1=left, 2=right). */ + method void setDirection(int Adirection) { + let direction = Adirection; + return; + } + + /** Returns the bat's left edge. */ + method int getLeft() { + return x; + } + + /** Returns the bat's right edge. */ + method int getRight() { + return x + width; + } + + /** Sets the bat's width. */ + method void setWidth(int Awidth) { + do hide(); + let width = Awidth; + do show(); + return; + } + + /** Moves the bat one step in the bat's direction. */ + method void move() { + if (direction = 1) { + let x = x - 4; + if (x < 0) { let x = 0; } + do Screen.setColor(false); + do Screen.drawRectangle((x + width) + 1, y, (x + width) + 4, y + height); + do Screen.setColor(true); + do Screen.drawRectangle(x, y, x + 3, y + height); + } + else { + let x = x + 4; + if ((x + width) > 511) { let x = 511 - width; } + do Screen.setColor(false); + do Screen.drawRectangle(x - 4, y, x - 1, y + height); + do Screen.setColor(true); + do Screen.drawRectangle((x + width) - 3, y, x + width, y + height); + } + return; + } +} diff --git a/simulator/src/jack/resources/test/Pong/Bat.vm b/simulator/src/jack/resources/test/Pong/Bat.vm new file mode 100644 index 000000000..194187273 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Bat.vm @@ -0,0 +1,204 @@ +function Bat.new 0 + push constant 5 + call Memory.alloc 1 + pop pointer 0 + push argument 0 + pop this 0 + push argument 1 + pop this 1 + push argument 2 + pop this 2 + push argument 3 + pop this 3 + push constant 2 + pop this 4 + push pointer 0 + call Bat.show 1 + pop temp 0 + push pointer 0 + return +function Bat.dispose 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function Bat.show 0 + push argument 0 + pop pointer 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push pointer 0 + call Bat.draw 1 + pop temp 0 + push constant 0 + return +function Bat.hide 0 + push argument 0 + pop pointer 0 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push pointer 0 + call Bat.draw 1 + pop temp 0 + push constant 0 + return +function Bat.draw 0 + push argument 0 + pop pointer 0 + push this 0 + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 3 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 0 + return +function Bat.setDirection 0 + push argument 0 + pop pointer 0 + push argument 1 + pop this 4 + push constant 0 + return +function Bat.getLeft 0 + push argument 0 + pop pointer 0 + push this 0 + return +function Bat.getRight 0 + push argument 0 + pop pointer 0 + push this 0 + push this 2 + add + return +function Bat.setWidth 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Bat.hide 1 + pop temp 0 + push argument 1 + pop this 2 + push pointer 0 + call Bat.show 1 + pop temp 0 + push constant 0 + return +function Bat.move 0 + push argument 0 + pop pointer 0 + push this 4 + push constant 1 + eq + not + if-goto Bat_0 + push this 0 + push constant 4 + sub + pop this 0 + push this 0 + push constant 0 + lt + not + if-goto Bat_1 + push constant 0 + pop this 0 +label Bat_1 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 2 + add + push constant 1 + add + push this 1 + push this 0 + push this 2 + add + push constant 4 + add + push this 1 + push this 3 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push constant 3 + add + push this 1 + push this 3 + add + call Screen.drawRectangle 4 + pop temp 0 + goto Bat_2 +label Bat_0 + push this 0 + push constant 4 + add + pop this 0 + push this 0 + push this 2 + add + push constant 511 + gt + not + if-goto Bat_3 + push constant 511 + push this 2 + sub + pop this 0 +label Bat_3 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push constant 4 + sub + push this 1 + push this 0 + push constant 1 + sub + push this 1 + push this 3 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 2 + add + push constant 3 + sub + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 3 + add + call Screen.drawRectangle 4 + pop temp 0 +label Bat_2 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Pong/Main.jack b/simulator/src/jack/resources/test/Pong/Main.jack new file mode 100644 index 000000000..9e65ceb73 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Main.jack @@ -0,0 +1,18 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Pong/Main.jack +/** + * Main class of the Pong game. + */ +class Main { + /** Initializes a Pong game and starts running it. */ + function void main() { + var PongGame game; + do PongGame.newInstance(); + let game = PongGame.getInstance(); + do game.run(); + do game.dispose(); + return; + } +} diff --git a/simulator/src/jack/resources/test/Pong/Main.vm b/simulator/src/jack/resources/test/Pong/Main.vm new file mode 100644 index 000000000..ef29075b6 --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/Main.vm @@ -0,0 +1,13 @@ +function Main.main 1 + call PongGame.newInstance 0 + pop temp 0 + call PongGame.getInstance 0 + pop local 0 + push local 0 + call PongGame.run 1 + pop temp 0 + push local 0 + call PongGame.dispose 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Pong/PongGame.jack b/simulator/src/jack/resources/test/Pong/PongGame.jack new file mode 100644 index 000000000..515ead4af --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/PongGame.jack @@ -0,0 +1,136 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Pong/PongGame.jack +/** + * Represents a Pong game. + */ +class PongGame { + + static PongGame instance; // A Pong game + field Bat bat; // bat + field Ball ball; // ball + field int wall; // current wall that the ball is bouncing off of + field boolean exit; // true when the game is over + field int score; // current score + field int lastWall; // last wall that the ball bounced off of + + // The current width of the bat + field int batWidth; + + /** Constructs a new Pong game. */ + constructor PongGame new() { + do Screen.clearScreen(); + let batWidth = 50; // initial bat size + let bat = Bat.new(230, 229, batWidth, 7); + let ball = Ball.new(253, 222, 0, 511, 0, 229); + do ball.setDestination(400,0); + do Screen.drawRectangle(0, 238, 511, 240); + do Output.moveCursor(22,0); + do Output.printString("Score: 0"); + + let exit = false; + let score = 0; + let wall = 0; + let lastWall = 0; + + return this; + } + + /** Deallocates the object's memory. */ + method void dispose() { + do bat.dispose(); + do ball.dispose(); + do Memory.deAlloc(this); + return; + } + + /** Creates an instance of a Pong game. */ + function void newInstance() { + let instance = PongGame.new(); + return; + } + + /** Returns this Pong game. */ + function PongGame getInstance() { + return instance; + } + + /** Starts the game, and handles inputs from the user that control + * the bat's movement direction. */ + method void run() { + var char key; + + while (~exit) { + // waits for a key to be pressed. + while ((key = 0) & (~exit)) { + let key = Keyboard.keyPressed(); + do bat.move(); + do moveBall(); + do Sys.wait(50); + } + + if (key = 130) { do bat.setDirection(1); } + else { + if (key = 132) { do bat.setDirection(2); } + else { + if (key = 140) { let exit = true; } + } + } + + // Waits for the key to be released. + while ((~(key = 0)) & (~exit)) { + let key = Keyboard.keyPressed(); + do bat.move(); + do moveBall(); + do Sys.wait(50); + } + } + + if (exit) { + do Output.moveCursor(10,27); + do Output.printString("Game Over"); + } + + return; + } + + /** + * Handles ball movement, including bouncing. + * If the ball bounces off a wall, finds its new direction. + * If the ball bounces off the bat, increases the score by one + * and shrinks the bat's size, to make the game more challenging. + */ + method void moveBall() { + var int bouncingDirection, batLeft, batRight, ballLeft, ballRight; + + let wall = ball.move(); + + if ((wall > 0) & (~(wall = lastWall))) { + let lastWall = wall; + let bouncingDirection = 0; + let batLeft = bat.getLeft(); + let batRight = bat.getRight(); + let ballLeft = ball.getLeft(); + let ballRight = ball.getRight(); + + if (wall = 4) { + let exit = (batLeft > ballRight) | (batRight < ballLeft); + if (~exit) { + if (ballRight < (batLeft + 10)) { let bouncingDirection = -1; } + else { + if (ballLeft > (batRight - 10)) { let bouncingDirection = 1; } + } + + let batWidth = batWidth - 2; + do bat.setWidth(batWidth); + let score = score + 1; + do Output.moveCursor(22,7); + do Output.printInt(score); + } + } + do ball.bounce(bouncingDirection); + } + return; + } +} \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Pong/PongGame.vm b/simulator/src/jack/resources/test/Pong/PongGame.vm new file mode 100644 index 000000000..e2b41418b --- /dev/null +++ b/simulator/src/jack/resources/test/Pong/PongGame.vm @@ -0,0 +1,309 @@ +function PongGame.new 0 + push constant 7 + call Memory.alloc 1 + pop pointer 0 + call Screen.clearScreen 0 + pop temp 0 + push constant 50 + pop this 6 + push constant 230 + push constant 229 + push this 6 + push constant 7 + call Bat.new 4 + pop this 0 + push constant 253 + push constant 222 + push constant 0 + push constant 511 + push constant 0 + push constant 229 + call Ball.new 6 + pop this 1 + push this 1 + push constant 400 + push constant 0 + call Ball.setDestination 3 + pop temp 0 + push constant 0 + push constant 238 + push constant 511 + push constant 240 + call Screen.drawRectangle 4 + pop temp 0 + push constant 22 + push constant 0 + call Output.moveCursor 2 + pop temp 0 + push constant 8 + call String.new 1 + push constant 83 + call String.appendChar 2 + push constant 99 + call String.appendChar 2 + push constant 111 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 58 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 48 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 + push constant 0 + pop this 3 + push constant 0 + pop this 4 + push constant 0 + pop this 2 + push constant 0 + pop this 5 + push pointer 0 + return +function PongGame.dispose 0 + push argument 0 + pop pointer 0 + push this 0 + call Bat.dispose 1 + pop temp 0 + push this 1 + call Ball.dispose 1 + pop temp 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function PongGame.newInstance 0 + call PongGame.new 0 + pop static 0 + push constant 0 + return +function PongGame.getInstance 0 + push static 0 + return +function PongGame.run 1 + push argument 0 + pop pointer 0 +label PongGame_0 + push this 3 + not + not + if-goto PongGame_1 +label PongGame_2 + push local 0 + push constant 0 + eq + push this 3 + not + and + not + if-goto PongGame_3 + call Keyboard.keyPressed 0 + pop local 0 + push this 0 + call Bat.move 1 + pop temp 0 + push pointer 0 + call PongGame.moveBall 1 + pop temp 0 + push constant 50 + call Sys.wait 1 + pop temp 0 + goto PongGame_2 +label PongGame_3 + push local 0 + push constant 130 + eq + not + if-goto PongGame_4 + push this 0 + push constant 1 + call Bat.setDirection 2 + pop temp 0 + goto PongGame_5 +label PongGame_4 + push local 0 + push constant 132 + eq + not + if-goto PongGame_6 + push this 0 + push constant 2 + call Bat.setDirection 2 + pop temp 0 + goto PongGame_7 +label PongGame_6 + push local 0 + push constant 140 + eq + not + if-goto PongGame_8 + push constant 1 + neg + pop this 3 +label PongGame_8 +label PongGame_7 +label PongGame_5 +label PongGame_9 + push local 0 + push constant 0 + eq + not + push this 3 + not + and + not + if-goto PongGame_10 + call Keyboard.keyPressed 0 + pop local 0 + push this 0 + call Bat.move 1 + pop temp 0 + push pointer 0 + call PongGame.moveBall 1 + pop temp 0 + push constant 50 + call Sys.wait 1 + pop temp 0 + goto PongGame_9 +label PongGame_10 + goto PongGame_0 +label PongGame_1 + push this 3 + not + if-goto PongGame_11 + push constant 10 + push constant 27 + call Output.moveCursor 2 + pop temp 0 + push constant 9 + call String.new 1 + push constant 71 + call String.appendChar 2 + push constant 97 + call String.appendChar 2 + push constant 109 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 32 + call String.appendChar 2 + push constant 79 + call String.appendChar 2 + push constant 118 + call String.appendChar 2 + push constant 101 + call String.appendChar 2 + push constant 114 + call String.appendChar 2 + call Output.printString 1 + pop temp 0 +label PongGame_11 + push constant 0 + return +function PongGame.moveBall 5 + push argument 0 + pop pointer 0 + push this 1 + call Ball.move 1 + pop this 2 + push this 2 + push constant 0 + gt + push this 2 + push this 5 + eq + not + and + not + if-goto PongGame_12 + push this 2 + pop this 5 + push constant 0 + pop local 0 + push this 0 + call Bat.getLeft 1 + pop local 1 + push this 0 + call Bat.getRight 1 + pop local 2 + push this 1 + call Ball.getLeft 1 + pop local 3 + push this 1 + call Ball.getRight 1 + pop local 4 + push this 2 + push constant 4 + eq + not + if-goto PongGame_13 + push local 1 + push local 4 + gt + push local 2 + push local 3 + lt + or + pop this 3 + push this 3 + not + not + if-goto PongGame_14 + push local 4 + push local 1 + push constant 10 + add + lt + not + if-goto PongGame_15 + push constant 1 + neg + pop local 0 + goto PongGame_16 +label PongGame_15 + push local 3 + push local 2 + push constant 10 + sub + gt + not + if-goto PongGame_17 + push constant 1 + pop local 0 +label PongGame_17 +label PongGame_16 + push this 6 + push constant 2 + sub + pop this 6 + push this 0 + push this 6 + call Bat.setWidth 2 + pop temp 0 + push this 4 + push constant 1 + add + pop this 4 + push constant 22 + push constant 7 + call Output.moveCursor 2 + pop temp 0 + push this 4 + call Output.printInt 1 + pop temp 0 +label PongGame_14 +label PongGame_13 + push this 1 + push local 0 + call Ball.bounce 2 + pop temp 0 +label PongGame_12 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Square/Main.jack b/simulator/src/jack/resources/test/Square/Main.jack new file mode 100644 index 000000000..bc6732e0a --- /dev/null +++ b/simulator/src/jack/resources/test/Square/Main.jack @@ -0,0 +1,15 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Square/Main.jack + +/** Initializes a new Square game and starts running it. */ +class Main { + function void main() { + var SquareGame game; + let game = SquareGame.new(); + do game.run(); + do game.dispose(); + return; + } +} diff --git a/simulator/src/jack/resources/test/Square/Main.vm b/simulator/src/jack/resources/test/Square/Main.vm new file mode 100644 index 000000000..d4f170a73 --- /dev/null +++ b/simulator/src/jack/resources/test/Square/Main.vm @@ -0,0 +1,11 @@ +function Main.main 1 + call SquareGame.new 0 + pop local 0 + push local 0 + call SquareGame.run 1 + pop temp 0 + push local 0 + call SquareGame.dispose 1 + pop temp 0 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Square/Square.jack b/simulator/src/jack/resources/test/Square/Square.jack new file mode 100644 index 000000000..e4a6590dc --- /dev/null +++ b/simulator/src/jack/resources/test/Square/Square.jack @@ -0,0 +1,113 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/9/Square/Square.jack + +/** Implements a graphical square. + The square has top-left x and y coordinates, and a size. */ +class Square { + + field int x, y; // screen location of the top-left corner of this square + field int size; // length of this square, in pixels + + /** Constructs and draws a new square with a given location and size. */ + constructor Square new(int ax, int ay, int asize) { + let x = ax; + let y = ay; + let size = asize; + do draw(); + return this; + } + + /** Disposes this square. */ + method void dispose() { + do Memory.deAlloc(this); + return; + } + + /** Draws this square in its current (x,y) location */ + method void draw() { + // Draws the square using the color black + do Screen.setColor(true); + do Screen.drawRectangle(x, y, x + size, y + size); + return; + } + + /** Erases this square. */ + method void erase() { + // Draws the square using the color white (background color) + do Screen.setColor(false); + do Screen.drawRectangle(x, y, x + size, y + size); + return; + } + + /** Increments the square size by 2 pixels (if possible). */ + method void incSize() { + if (((y + size) < 254) & ((x + size) < 510)) { + do erase(); + let size = size + 2; + do draw(); + } + return; + } + + /** Decrements the square size by 2 pixels (if possible). */ + method void decSize() { + if (size > 2) { + do erase(); + let size = size - 2; + do draw(); + } + return; + } + + /** Moves this square up by 2 pixels (if possible). */ + method void moveUp() { + if (y > 1) { + // Erases the bottom two rows of this square in its current location + do Screen.setColor(false); + do Screen.drawRectangle(x, (y + size) - 1, x + size, y + size); + let y = y - 2; + // Draws the top two rows of this square in its new location + do Screen.setColor(true); + do Screen.drawRectangle(x, y, x + size, y + 1); + } + return; + } + + /** Moves the square down by 2 pixels (if possible). */ + method void moveDown() { + if ((y + size) < 254) { + do Screen.setColor(false); + do Screen.drawRectangle(x, y, x + size, y + 1); + let y = y + 2; + do Screen.setColor(true); + do Screen.drawRectangle(x, (y + size) - 1, x + size, y + size); + } + return; + } + + /** Moves the square left by 2 pixels (if possible). */ + method void moveLeft() { + if (x > 1) { + do Screen.setColor(false); + do Screen.drawRectangle((x + size) - 1, y, x + size, y + size); + let x = x - 2; + do Screen.setColor(true); + do Screen.drawRectangle(x, y, x + 1, y + size); + } + return; + } + + /** Moves the square right by 2 pixels (if possible). */ + method void moveRight() { + if ((x + size) < 510) { + do Screen.setColor(false); + do Screen.drawRectangle(x, y, x + 1, y + size); + let x = x + 2; + do Screen.setColor(true); + do Screen.drawRectangle((x + size) - 1, y, x + size, y + size); + } + return; + } +} diff --git a/simulator/src/jack/resources/test/Square/Square.vm b/simulator/src/jack/resources/test/Square/Square.vm new file mode 100644 index 000000000..74d170995 --- /dev/null +++ b/simulator/src/jack/resources/test/Square/Square.vm @@ -0,0 +1,298 @@ +function Square.new 0 + push constant 3 + call Memory.alloc 1 + pop pointer 0 + push argument 0 + pop this 0 + push argument 1 + pop this 1 + push argument 2 + pop this 2 + push pointer 0 + call Square.draw 1 + pop temp 0 + push pointer 0 + return +function Square.dispose 0 + push argument 0 + pop pointer 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function Square.draw 0 + push argument 0 + pop pointer 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 0 + return +function Square.erase 0 + push argument 0 + pop pointer 0 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 + push constant 0 + return +function Square.incSize 0 + push argument 0 + pop pointer 0 + push this 1 + push this 2 + add + push constant 254 + lt + push this 0 + push this 2 + add + push constant 510 + lt + and + not + if-goto Square_0 + push pointer 0 + call Square.erase 1 + pop temp 0 + push this 2 + push constant 2 + add + pop this 2 + push pointer 0 + call Square.draw 1 + pop temp 0 +label Square_0 + push constant 0 + return +function Square.decSize 0 + push argument 0 + pop pointer 0 + push this 2 + push constant 2 + gt + not + if-goto Square_1 + push pointer 0 + call Square.erase 1 + pop temp 0 + push this 2 + push constant 2 + sub + pop this 2 + push pointer 0 + call Square.draw 1 + pop temp 0 +label Square_1 + push constant 0 + return +function Square.moveUp 0 + push argument 0 + pop pointer 0 + push this 1 + push constant 1 + gt + not + if-goto Square_2 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 2 + add + push constant 1 + sub + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 + push this 1 + push constant 2 + sub + pop this 1 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push this 2 + add + push this 1 + push constant 1 + add + call Screen.drawRectangle 4 + pop temp 0 +label Square_2 + push constant 0 + return +function Square.moveDown 0 + push argument 0 + pop pointer 0 + push this 1 + push this 2 + add + push constant 254 + lt + not + if-goto Square_3 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push this 2 + add + push this 1 + push constant 1 + add + call Screen.drawRectangle 4 + pop temp 0 + push this 1 + push constant 2 + add + pop this 1 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 2 + add + push constant 1 + sub + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 +label Square_3 + push constant 0 + return +function Square.moveLeft 0 + push argument 0 + pop pointer 0 + push this 0 + push constant 1 + gt + not + if-goto Square_4 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 2 + add + push constant 1 + sub + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 + push this 0 + push constant 2 + sub + pop this 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push constant 1 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 +label Square_4 + push constant 0 + return +function Square.moveRight 0 + push argument 0 + pop pointer 0 + push this 0 + push this 2 + add + push constant 510 + lt + not + if-goto Square_5 + push constant 0 + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 1 + push this 0 + push constant 1 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 + push this 0 + push constant 2 + add + pop this 0 + push constant 1 + neg + call Screen.setColor 1 + pop temp 0 + push this 0 + push this 2 + add + push constant 1 + sub + push this 1 + push this 0 + push this 2 + add + push this 1 + push this 2 + add + call Screen.drawRectangle 4 + pop temp 0 +label Square_5 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/resources/test/Square/SquareGame.jack b/simulator/src/jack/resources/test/Square/SquareGame.jack new file mode 100644 index 000000000..fa4ce93e8 --- /dev/null +++ b/simulator/src/jack/resources/test/Square/SquareGame.jack @@ -0,0 +1,76 @@ +// This file is part of www.nand2tetris.org +// and the book "The Elements of Computing Systems" +// by Nisan and Schocken, MIT Press. +// File name: projects/09/Square/SquareGame.jack +/** + * Implements the Square game. + * This simple game allows the user to move a black square around + * the screen, and change the square's size during the movement. + * When the game starts, a square of 30 by 30 pixels is shown at the + * top-left corner of the screen. The user controls the square as follows. + * The 4 arrow keys are used to move the square up, down, left, and right. + * The 'z' and 'x' keys are used, respectively, to decrement and increment + * the square's size. The 'q' key is used to quit the game. + */ +class SquareGame { + field Square square; // the square of this game + field int direction; // the square's current direction: + // 0=none, 1=up, 2=down, 3=left, 4=right + + /** Constructs a new square game. */ + constructor SquareGame new() { + // The initial square is located in (0,0), has size 30, and is not moving. + let square = Square.new(0, 0, 30); + let direction = 0; + return this; + } + + /** Disposes this game. */ + method void dispose() { + do square.dispose(); + do Memory.deAlloc(this); + return; + } + + /** Moves the square in the current direction. */ + method void moveSquare() { + if (direction = 1) { do square.moveUp(); } + if (direction = 2) { do square.moveDown(); } + if (direction = 3) { do square.moveLeft(); } + if (direction = 4) { do square.moveRight(); } + do Sys.wait(5); // delays the next movement + return; + } + + /** Runs the game: handles the user's inputs and moves the square accordingly */ + method void run() { + var char key; // the key currently pressed by the user + var boolean exit; + let exit = false; + + while (~exit) { + // waits for a key to be pressed + while (key = 0) { + let key = Keyboard.keyPressed(); + do moveSquare(); + } + if (key = 81) { let exit = true; } // q key + if (key = 90) { do square.decSize(); } // z key + if (key = 88) { do square.incSize(); } // x key + if (key = 131) { let direction = 1; } // up arrow + if (key = 133) { let direction = 2; } // down arrow + if (key = 130) { let direction = 3; } // left arrow + if (key = 132) { let direction = 4; } // right arrow + + // waits for the key to be released + while (~(key = 0)) { + let key = Keyboard.keyPressed(); + do moveSquare(); + } + } // while + return; + } +} + + + diff --git a/simulator/src/jack/resources/test/Square/SquareGame.vm b/simulator/src/jack/resources/test/Square/SquareGame.vm new file mode 100644 index 000000000..838c2b9f9 --- /dev/null +++ b/simulator/src/jack/resources/test/Square/SquareGame.vm @@ -0,0 +1,168 @@ +function SquareGame.new 0 + push constant 2 + call Memory.alloc 1 + pop pointer 0 + push constant 0 + push constant 0 + push constant 30 + call Square.new 3 + pop this 0 + push constant 0 + pop this 1 + push pointer 0 + return +function SquareGame.dispose 0 + push argument 0 + pop pointer 0 + push this 0 + call Square.dispose 1 + pop temp 0 + push pointer 0 + call Memory.deAlloc 1 + pop temp 0 + push constant 0 + return +function SquareGame.moveSquare 0 + push argument 0 + pop pointer 0 + push this 1 + push constant 1 + eq + not + if-goto SquareGame_0 + push this 0 + call Square.moveUp 1 + pop temp 0 +label SquareGame_0 + push this 1 + push constant 2 + eq + not + if-goto SquareGame_1 + push this 0 + call Square.moveDown 1 + pop temp 0 +label SquareGame_1 + push this 1 + push constant 3 + eq + not + if-goto SquareGame_2 + push this 0 + call Square.moveLeft 1 + pop temp 0 +label SquareGame_2 + push this 1 + push constant 4 + eq + not + if-goto SquareGame_3 + push this 0 + call Square.moveRight 1 + pop temp 0 +label SquareGame_3 + push constant 5 + call Sys.wait 1 + pop temp 0 + push constant 0 + return +function SquareGame.run 2 + push argument 0 + pop pointer 0 + push constant 0 + pop local 1 +label SquareGame_4 + push local 1 + not + not + if-goto SquareGame_5 +label SquareGame_6 + push local 0 + push constant 0 + eq + not + if-goto SquareGame_7 + call Keyboard.keyPressed 0 + pop local 0 + push pointer 0 + call SquareGame.moveSquare 1 + pop temp 0 + goto SquareGame_6 +label SquareGame_7 + push local 0 + push constant 81 + eq + not + if-goto SquareGame_8 + push constant 1 + neg + pop local 1 +label SquareGame_8 + push local 0 + push constant 90 + eq + not + if-goto SquareGame_9 + push this 0 + call Square.decSize 1 + pop temp 0 +label SquareGame_9 + push local 0 + push constant 88 + eq + not + if-goto SquareGame_10 + push this 0 + call Square.incSize 1 + pop temp 0 +label SquareGame_10 + push local 0 + push constant 131 + eq + not + if-goto SquareGame_11 + push constant 1 + pop this 1 +label SquareGame_11 + push local 0 + push constant 133 + eq + not + if-goto SquareGame_12 + push constant 2 + pop this 1 +label SquareGame_12 + push local 0 + push constant 130 + eq + not + if-goto SquareGame_13 + push constant 3 + pop this 1 +label SquareGame_13 + push local 0 + push constant 132 + eq + not + if-goto SquareGame_14 + push constant 4 + pop this 1 +label SquareGame_14 +label SquareGame_15 + push local 0 + push constant 0 + eq + not + not + if-goto SquareGame_16 + call Keyboard.keyPressed 0 + pop local 0 + push pointer 0 + call SquareGame.moveSquare 1 + pop temp 0 + goto SquareGame_15 +label SquareGame_16 + goto SquareGame_4 +label SquareGame_5 + push constant 0 + return \ No newline at end of file diff --git a/simulator/src/jack/symbol.table.test.ts b/simulator/src/jack/symbol.table.test.ts new file mode 100644 index 000000000..021c21c58 --- /dev/null +++ b/simulator/src/jack/symbol.table.test.ts @@ -0,0 +1,156 @@ +import { + LocalSymbolTable, + VariableSymbol, + ScopeType, + SubroutineScope, +} from "./symbol"; + +describe("Jack local symbol table", () => { + const jestConsole = console; + + beforeEach(() => { + global.console = require("console"); + }); + + afterEach(() => { + global.console = jestConsole; + }); + + test("add", () => { + const localSymbolTable = new LocalSymbolTable(); + const symbols = [ + { + name: "a1", + type: "int", + scope: ScopeType.Local, + index: 0, + } as VariableSymbol, + { + name: "a2", + type: "char", + scope: ScopeType.Local, + index: 1, + } as VariableSymbol, + + { + name: "a3", + type: "boolean", + scope: ScopeType.Argument, + index: 0, + } as VariableSymbol, + { + name: "a4", + type: "char", + scope: ScopeType.Argument, + index: 1, + } as VariableSymbol, + + { + name: "a5", + type: "int", + scope: ScopeType.This, + index: 0, + } as VariableSymbol, + { + name: "a6", + type: "String", + scope: ScopeType.This, + index: 1, + } as VariableSymbol, + + { + name: "a7", + type: "A", + scope: ScopeType.Static, + index: 0, + } as VariableSymbol, + { + name: "a8", + type: "B", + scope: ScopeType.Static, + index: 1, + } as VariableSymbol, + ]; + for (const s of symbols) { + if (s.scope == ScopeType.Argument) { + localSymbolTable.defineArgument(s.name, s.type, false); + } else { + localSymbolTable.define(s.scope, s.name, s.type); + } + } + for (const s of symbols) { + expect(localSymbolTable.lookup(s.name)).toEqual(s); + expect(localSymbolTable.lookup(s.name + "_")).toBeUndefined(); + } + expect(localSymbolTable.fieldsCount()).toBe(2); + }); + + test("shadowing", () => { + const localSymbolTable = new LocalSymbolTable(); + const symbols = [ + { + name: "a", + type: "char", + scope: ScopeType.This, + index: 0, + } as VariableSymbol, + { + name: "a", + type: "int", + scope: ScopeType.Local, + index: 0, + } as VariableSymbol, + ]; + for (const s of symbols) { + localSymbolTable.define(s.scope, s.name, s.type); + } + expect(localSymbolTable.lookup("a")).toEqual(symbols[1]); + }); + + test("pop stack", () => { + const localSymbolTable = new LocalSymbolTable(); + const symbols = [ + { + name: "a", + type: "int", + scope: ScopeType.Local, + index: 0, + } as VariableSymbol, + ]; + for (const s of symbols) { + localSymbolTable.define(s.scope, s.name, s.type); + } + localSymbolTable.popStack(); + expect(localSymbolTable.lookup("a")).toBeUndefined(); + }); + + test("function vars", () => { + const localSymbolTable = new LocalSymbolTable(); + const symbols = [ + { + name: "a", + type: "int", + scope: ScopeType.Local, + index: 0, + } as VariableSymbol, + { + name: "b", + type: "char", + scope: ScopeType.Argument, + index: 1, + } as VariableSymbol, + ]; + + for (const s of symbols) { + if (s.scope == ScopeType.Argument) { + localSymbolTable.defineArgument(s.name, s.type, true); + } else { + localSymbolTable.define(s.scope, s.name, s.type); + } + } + expect(localSymbolTable.popStack()).toEqual({ + arguments: [symbols[1]], + locals: [symbols[0]], + } as SubroutineScope); + }); +}); diff --git a/simulator/src/jack/symbol.ts b/simulator/src/jack/symbol.ts new file mode 100644 index 000000000..e1050d252 --- /dev/null +++ b/simulator/src/jack/symbol.ts @@ -0,0 +1,136 @@ +/** + * Generic symbol. Can be used for both class and function symbols + */ +export const SubroutineType = { + Constructor: 1, + Function: 2, + Method: 3, +} as const; +export type SubroutineType = + (typeof SubroutineType)[keyof typeof SubroutineType]; +export interface SubroutineInfo { + paramsCount: number; + type: SubroutineType; + localVarsCount?: number; +} + +export type GlobalSymbolTable = Record; +/** + * Symbol that represents class or a subroutine + */ +export interface GenericSymbol { + subroutineInfo?: SubroutineInfo; +} +export function createSubroutineSymbol( + paramsCount: number, + type: SubroutineType, + localVarsCount?: number, +): GenericSymbol { + const s = { paramsCount, type } as SubroutineInfo; + if (localVarsCount != undefined) { + s.localVarsCount = localVarsCount; + } + return { subroutineInfo: s } as GenericSymbol; +} + +type VariableType = string; + +export const ScopeType = { + Static: 1, + This: 2, + Argument: 3, + Local: 4, +}; +export type ScopeType = (typeof ScopeType)[keyof typeof ScopeType]; + +const scopeTypeToStringMap: Record = { + [ScopeType.Static]: "static", + [ScopeType.This]: "this", + [ScopeType.Argument]: "argument", + [ScopeType.Local]: "local", +}; +export function scopeTypeToString(scopeType: ScopeType): string { + if (scopeTypeToStringMap[scopeType] === undefined) { + throw new Error(`Unknown scope type: ${scopeType}`); + } + return scopeTypeToStringMap[scopeType]; +} +export type VariableSymbol = { + name: string; + type: VariableType; + scope: ScopeType; + index: number; +}; + +export type SubroutineScope = { + arguments: VariableSymbol[]; + locals: VariableSymbol[]; +}; +/** + * Symbol table that provides lookup for variables in different scopes in a file + */ +export class LocalSymbolTable { + private scopes: Record = { + [ScopeType.Static]: [], + [ScopeType.This]: [], + [ScopeType.Argument]: [], + [ScopeType.Local]: [], + }; + private static scopesLookupOrder = [ + ScopeType.Local, + ScopeType.Argument, + ScopeType.This, + ScopeType.Static, + ]; + lookup(name: string): VariableSymbol | undefined { + for (const scope of LocalSymbolTable.scopesLookupOrder) { + const symbol = this.scopes[scope].find((v) => v.name == name); + if (symbol != undefined) return symbol; + } + return undefined; + } + defineArgument(name: string, type: VariableType, inMethod: boolean) { + let index = this.scopes[ScopeType.Argument].length; + if (inMethod) { + //in a method first arg is this + index++; + } + this.scopes[ScopeType.Argument].push({ + name, + type, + scope: ScopeType.Argument, + index, + }); + } + //define symbol in scope + define(scope: ScopeType, varName: string, type: VariableType) { + if (scope == ScopeType.Argument) { + throw new Error( + "Please use defineArgument method to define function arguments", + ); + } + this.scopes[scope].push({ + name: varName, + type, + scope, + index: this.scopes[scope].length, + }); + } + + popStack() { + const f = { + arguments: this.scopes[ScopeType.Argument], + locals: this.scopes[ScopeType.Local], + } as SubroutineScope; + this.scopes[ScopeType.Local] = []; + this.scopes[ScopeType.Argument] = []; + return f; + } + setSubroutineScope(subroutineScope: SubroutineScope) { + this.scopes[ScopeType.Argument] = subroutineScope.arguments; + this.scopes[ScopeType.Local] = subroutineScope.locals; + } + fieldsCount() { + return this.scopes[ScopeType.This].length; + } +} diff --git a/simulator/src/jack/test.helper.ts b/simulator/src/jack/test.helper.ts new file mode 100644 index 000000000..44cef7ae4 --- /dev/null +++ b/simulator/src/jack/test.helper.ts @@ -0,0 +1,83 @@ +import fs from "fs"; +import path from "path"; +import { JackCompilerError } from "./error"; +import { CustomErrorListener } from "./listener/error.listener"; +import { JackParser, ProgramContext } from "./generated/JackParser"; +import { + CharStream, + CommonTokenStream, + ParseTreeListener, + ParseTreeWalker, +} from "antlr4ng"; +import { JackLexer } from "./generated/JackLexer"; + +export function parseJackFile(filePath: string, trace = false) { + const errorListener = new CustomErrorListener(); + const f = fs.readFileSync(filePath, "utf8"); + return parseJackText(f, errorListener, trace); +} + +export function parseJackText( + src: string, + errorListener?: CustomErrorListener, + trace = false, + throwOnErrors = true, +): ProgramContext { + if (errorListener === undefined) { + errorListener = new CustomErrorListener(); + } + const inputStream = CharStream.fromString(src); + const lexer = new JackLexer(inputStream); + if (errorListener) { + lexer.removeErrorListeners(); + lexer.addErrorListener(errorListener); + } + + const tokenStream = new CommonTokenStream(lexer); + const parser = new JackParser(tokenStream); + if (errorListener != undefined) { + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); + } + const tree = parser.program(); + + expect(tokenStream.getTokens().length).toBeGreaterThan(0); + if (errorListener.errors.length > 0) { + console.error("Parser or lexer errors found"); + handleErrors(src, errorListener.errors); + } + return tree; +} + +export function getTestResourcePath(relativePath: string) { + return path.join(__dirname, "resources", "test", relativePath); +} + +export function listenToTheTree( + tree: ProgramContext, + listener: T, +) { + ParseTreeWalker.DEFAULT.walk(listener, tree); + return listener; +} + +export function handleErrors(src: string, errors: JackCompilerError[]) { + const msg = errors + .map((e) => { + return `${e.span.line}:${e.span.start} ${e.msg}\n${src.split("\n")[e.span.line]}`; + }) + .join("\n"); + console.error(msg); + throw new Error(msg); +} + +export const testResourceDirs: string[] = [ + "Average", + "ConvertToBin", + "Fraction", + "HelloWorld", + "List", + "Pong", + "Square", + "ComplexArrays", +]; diff --git a/simulator/src/languages/grammars/JackLexer.g4 b/simulator/src/languages/grammars/JackLexer.g4 new file mode 100644 index 000000000..d0ced09a5 --- /dev/null +++ b/simulator/src/languages/grammars/JackLexer.g4 @@ -0,0 +1,59 @@ +lexer grammar JackLexer; + +CLASS: 'class'; +CONSTRUCTOR: 'constructor'; +FUNCTION: 'function'; +METHOD: 'method'; +FIELD: 'field'; +STATIC: 'static'; +VAR: 'var'; +INT: 'int'; +CHAR: 'char'; +BOOLEAN: 'boolean'; +VOID: 'void'; + +LET: 'let'; +DO: 'do'; +IF: 'if'; +ELSE: 'else'; +WHILE: 'while'; +RETURN: 'return'; + +LBRACE: '{'; +RBRACE: '}'; +LPAREN: '('; +RPAREN: ')'; +LBRACKET: '['; +RBRACKET: ']'; +DOT: '.'; +COMMA: ','; +SEMICOLON: ';'; + +EQUALS: '='; +PLUS: '+'; +MINUS: '-'; +MUL: '*'; +DIV: '/'; +AND: '&'; +OR: '|'; +TILDE: '~'; +LESS_THAN: '<'; +GREATER_THAN: '>'; + +WHITESPACE: [ \t\r\n\f]+ -> channel(HIDDEN); +BLOCK_COMMENT: '/*' .*? ('*/' | EOF) -> channel(HIDDEN); +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +INTEGER_LITERAL: [0-9]+; +TRUE: 'true'; +FALSE: 'false'; +NULL_LITERAL: 'null'; +THIS_LITERAL: 'this'; + +IDENTIFIER: [a-zA-Z_] [a-zA-Z0-9_]*; + + + +STRING_LITERAL: '"' (~["\r\n])* '"'; +// STRING_LITERAL: UnterminatedStringLiteral '"'; +// UnterminatedStringLiteral: '"' ~["\\\r\n]*; \ No newline at end of file diff --git a/simulator/src/languages/grammars/JackParser.g4 b/simulator/src/languages/grammars/JackParser.g4 new file mode 100644 index 000000000..bcddbdeba --- /dev/null +++ b/simulator/src/languages/grammars/JackParser.g4 @@ -0,0 +1,99 @@ +parser grammar JackParser; + +options { + tokenVocab = JackLexer; +} +@header { +import { SubroutineScope, LocalSymbolTable } from "../symbol"; +} + +program: classDeclaration EOF; +classDeclaration + locals[localSymbolTable: LocalSymbolTable | undefined ]: + CLASS className LBRACE classVarDec* subroutineDeclaration* rBrace; +className: IDENTIFIER; +classVarDec: (STATIC | FIELD) fieldList SEMICOLON; +fieldList: varType fieldName ( COMMA fieldName)*; +fieldName: IDENTIFIER; +subroutineDeclaration + locals[symbols: SubroutineScope | undefined]: + subroutineType subroutineDecWithoutType; +subroutineType: CONSTRUCTOR | METHOD | FUNCTION; +subroutineDecWithoutType: + subroutineReturnType subroutineName LPAREN parameterList RPAREN subroutineBody; +subroutineName: IDENTIFIER; +subroutineReturnType: varType | VOID; + +varType: INT | CHAR | BOOLEAN | IDENTIFIER; + +parameterList: (parameter (COMMA parameter)*)?; +parameter: varType parameterName; +parameterName: IDENTIFIER; +subroutineBody: LBRACE varDeclaration* statements rBrace; +rBrace: RBRACE; +varDeclaration: + VAR varType varNameInDeclaration (COMMA varNameInDeclaration)* SEMICOLON; +varNameInDeclaration: IDENTIFIER; +statements: statement*; +statement: + letStatement + | ifElseStatement + | whileStatement + | doStatement + | returnStatement; + +letStatement: + LET (varName | arrayAccess) equals expression SEMICOLON; +equals: EQUALS; +ifElseStatement + locals[endLabel:string=""]: ifStatement elseStatement?; +ifStatement + locals[endLabel:string=""]: + IF LPAREN ifExpression RPAREN LBRACE statements rBrace; +ifExpression: expression; +elseStatement: ELSE LBRACE statements rBrace; + +whileStatement + locals[startLabel:string="";endLabel:string="";]: + WHILE LPAREN whileExpression RPAREN LBRACE statements rBrace; +whileExpression: expression; +doStatement: DO subroutineCall SEMICOLON; + +subroutineCall: subroutineId LPAREN expressionList RPAREN; +subroutineId: ((className | THIS_LITERAL) DOT)? subroutineName; +returnStatement: RETURN expression? SEMICOLON; + +expressionList: (expression (COMMA expression)*)?; + +expression: + constant + | varName + | subroutineCall + | arrayAccess + | unaryOperation + | expression binaryOperator expression + | groupedExpression; + +constant: + INTEGER_LITERAL + | STRING_LITERAL + | booleanLiteral + | NULL_LITERAL + | THIS_LITERAL; +varName: IDENTIFIER; +arrayAccess: varName LBRACKET expression RBRACKET; +unaryOperation: unaryOperator expression; +groupedExpression: LPAREN expression RPAREN; + +booleanLiteral: TRUE | FALSE; +unaryOperator: TILDE | MINUS; +binaryOperator: + PLUS + | MINUS + | MUL + | DIV + | AND + | OR + | LESS_THAN + | GREATER_THAN + | EQUALS; \ No newline at end of file diff --git a/web/src/pages/compiler.tsx b/web/src/pages/compiler.tsx index af2de31c6..aac814c96 100644 --- a/web/src/pages/compiler.tsx +++ b/web/src/pages/compiler.tsx @@ -129,7 +129,7 @@ export const Compiler = () => { const onCreateFile = async (name?: string) => { if (name) { - await actions.writeFile(name); + await actions.writeNewFile(name); onSelect(name); setSuppressStatus(false); } @@ -218,6 +218,7 @@ export const Compiler = () => { value={state.files[file]} onChange={(source: string) => { actions.writeFile(file, source); + actions.validate(); }} error={state.compiled[file].error} language={"jack"}