-
Notifications
You must be signed in to change notification settings - Fork 45
Feat/compiler #483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/compiler #483
Changes from all commits
95131df
53b5a0b
1f3b3f4
240aedc
3d6f8be
49075d7
f9dab02
029f124
d07a587
3a69078
b541d22
ab1e0c9
59f7507
730f72e
d61c6ca
2fb01eb
81193e2
04d720f
1511c94
c40bfdd
32b91f7
d612816
5716258
aee5ec7
fec5790
3c1185a
af250f2
cf2c04c
ecadfb2
be5142a
1b9a348
afbf33b
faef67d
dd918cb
ae65cc5
694dbcd
6bd99b8
d67729e
375001d
a170adf
aa8a8ca
e8e9dd8
4a51caf
ae414f2
771c2cd
d92b385
4ae5997
d5c7f27
f1c9ea3
560f043
1c0c9ab
2aba5fb
d27f391
ae9be57
1317635
06432ba
46998c6
4970648
8030509
c05caa9
074b74a
f4d54d8
c5d8243
dbf7d80
8681e06
6c5ec41
98f696e
12edbb1
1dfc0ce
70cbcec
30f1b9c
ce31d3a
bf7acd0
179a39c
3e35604
7604ef5
773cf3d
7c154b9
4bcce0a
8b71fdb
b573d00
7924123
8afba76
2dbf34d
a68a248
a2620ec
d41d95e
078f95b
520ce31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,3 +24,6 @@ out | |
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
**/.vscode/numbered-bookmars.json | ||
**/.antlr | ||
.idea/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string>, | ||
dispatch: MutableRefObject<CompilerStoreDispatch>, | ||
|
@@ -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); | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Nit) empty blank line, and a couple others around the project. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the rule here? Can we add this to prettier? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please ignore the piece about prettier There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, unfortunately I don't think prettier supports it at this time. I believe Rome might? But I haven't looked into it closely. Broadly, separate top-level and class-level function declarations by one blank line. Top level and class level properties and variables can be grouped without blank lines between them "so that it looks good" - I know that's not a great rule, but there's room for a bit of aesthetics. "Keep similar things together" is the principle there, but not super strict. But for the rule - every function should have a blank line before it (or before its docblock, if it has a comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this what we are looking for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, it's a part of eslint, not prettier, that's probably why I couldn't find it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's agree that we can create another PR and standardize this across the whole codebase There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Please do your best in the meantime to keep this as tidy as you can. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me know if you want me to change something. Otherwise I will leave it as it is |
||
|
||
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<string, string | CompilationError>, | ||
) { | ||
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,15 +155,29 @@ export function makeCompilerStore( | |
await fs.writeFile(`${name}.jack`, content); | ||
} | ||
}, | ||
async writeNewFile(name: string, content?: string) { | ||
happytomatoe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
content ??= classTemplate(name); | ||
dispatch.current({ | ||
action: "setFileAndValidate", | ||
payload: { name, content }, | ||
}); | ||
if (fs) { | ||
await fs.writeFile(`${name}.jack`, content); | ||
} | ||
}, | ||
|
||
async reset() { | ||
fs = undefined; | ||
dispatch.current({ action: "reset" }); | ||
}, | ||
|
||
async compile() { | ||
dispatch.current({ action: "compile" }); | ||
dispatch.current({ action: "writeCompiled" }); | ||
}, | ||
async validate() { | ||
dispatch.current({ action: "validate" }); | ||
}, | ||
}; | ||
|
||
const initialState: CompilerPageState = { | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,19 +22,24 @@ | |
"@nand2tetris/projects": "^1.0.0", | ||
"@nand2tetris/runner": "^1.0.0", | ||
"@types/node": "^20.14.2", | ||
"antlr4ng": "^3.0.7", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this change need a change in the README? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've already changed the README. Both of these libraries implement the same api/interfaces There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This library already has a ton of comments in typescript sources so I don't know if makes sense to add anything else in terms of documentation |
||
"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", | ||
"typescript": "^5.4.5" | ||
}, | ||
"scripts": { | ||
"build": "tsc", | ||
"test": "jest --verbose" | ||
"test": "jest --verbose", | ||
"test-jack": "npm run test -- -t \"Jack\"", | ||
DavidSouther marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"test-jack-w": "npm run test-w -- -t \"Jack\"", | ||
"gen": "cd src/languages/grammars && antlr4ng -Dlanguage=TypeScript JackLexer.g4 JackParser.g4 -o ../../jack/generated" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
Uh oh!
There was an error while loading. Please reload this page.