Finish readme and add pngs
This commit is contained in:
99
README.md
99
README.md
@@ -91,4 +91,103 @@ translate individual asm-files and does not search a directory.
|
|||||||
Load the resulting hack file into the CPU emulator to verify that the
|
Load the resulting hack file into the CPU emulator to verify that the
|
||||||
assembler works correctly.
|
assembler works correctly.
|
||||||
|
|
||||||
|
## Project 7/8: VM Translator
|
||||||
|
|
||||||
|
At this point, we have finished the hardware part of our computer and can
|
||||||
|
program it via the Hack assembly language. Of course, a higher-level programming
|
||||||
|
language is desirable to write programs more comfortably. Therefore, our goal
|
||||||
|
is to implement the high-level programming language *Jack* and compile it down
|
||||||
|
into Hack assembly.
|
||||||
|
|
||||||
|
We use a two-step compilation process. First, we translate the Jack code into VM
|
||||||
|
code, which we then translate further into Hack assembly. The goal of projects 7
|
||||||
|
and 8 is to implement the VM translator in a programming language of our choice.
|
||||||
|
Even though Python is my primary language, and I would have been faster using
|
||||||
|
it, I decided to opt for Rust, a language that I hadn't used before. Rust comes
|
||||||
|
with a flourishing eco-system and static typing that came in handy for this
|
||||||
|
project.
|
||||||
|
|
||||||
|
My VM translator ended up with just under 900 lines of code. A considerable part
|
||||||
|
of that is the template Hack assembly code that the translator fills with
|
||||||
|
registers and addresses. I would have opted for the Jinja2 templating language
|
||||||
|
in Python, but instead, I used in-code strings combined with "format!\". While
|
||||||
|
this looks ugly at times, it avoids external dependencies. The approach is okay
|
||||||
|
for this course, but a template engine would have been desirable for a
|
||||||
|
real-world project.
|
||||||
|
|
||||||
|
To run the VM translator, switch into the `./vm_translator directory` and
|
||||||
|
execute `cargo build`. You can then execute
|
||||||
|
`./vm_translator/target/debug/vm_translator` on one of the example projects in
|
||||||
|
`./projects/08`.
|
||||||
|
|
||||||
|
I was happy with my choice to learn Rust even though I spent too much time
|
||||||
|
figuring out trivial things like how to read a file.
|
||||||
|
|
||||||
|
## Project 9: High-Level Language
|
||||||
|
|
||||||
|
With the VM translator in place, it is now time to understand the high-level
|
||||||
|
Jack languaged by implementing a project. I decided to implement a 1D cellular
|
||||||
|
automation in `./projects/09/CellAutomaton1D`. To run the program first compile
|
||||||
|
the project by running `./tool/JackCompiler.sh ./projects/09/CellAutomaton1D`.
|
||||||
|
Then, start the Virtual Machine Emulator and open the project. Make sure to
|
||||||
|
select the directory `CellAutomaton1D` and not one of the VM files.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The program consists of two parts. First, you configure the initial population
|
||||||
|
using Vim key bindings, and then the program simulates the selected rule. You
|
||||||
|
have the option to pause and single step the simulation.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
I have also implemented Vim syntax highlighting for the jack programming
|
||||||
|
language. The best thing is that Vim yells at you when you forgot one of
|
||||||
|
the keywords, like let or do. And you will forget them all the time.
|
||||||
|
Simple put [jack.vim](vim/jack.vim) into your Vim's syntax
|
||||||
|
directory, and you should be good to go.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Project 10/11: Compiler
|
||||||
|
|
||||||
|
With a solid understanding of the Jack programming language, we are now ready to
|
||||||
|
implement the compiler. I opted for Rust again and had a great time. The
|
||||||
|
directory `jack_analyzer` contains the tokenizer and parser, which outputs an
|
||||||
|
XML syntax tree. In project 11, we use the analyzer to generate VM code. The
|
||||||
|
resulting Rust project is in the directory `jack_compiler`. To compile the
|
||||||
|
project, change into the directory and execute `cargo build`.
|
||||||
|
|
||||||
|
You can now recompile the cellular automaton by running the following command:
|
||||||
|
|
||||||
|
~~~
|
||||||
|
./jack_compiler/target/debug/jack_compiler projects/09/CellAutomaton1D
|
||||||
|
~~~
|
||||||
|
|
||||||
|
I honestly was happy with my decision to opt for Rust again. While the project
|
||||||
|
would undoubtedly have been manageable in Python, static typing made it so much
|
||||||
|
easier to cover all cases for keywords and tokens. The compiler has a little
|
||||||
|
over 1000 lines of code. That sounds pretty decent.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Project 12: The Operating System
|
||||||
|
|
||||||
|
With the compiler in place, the last step is implementing the OS functionality
|
||||||
|
that previously came from the Virtual Machine Emulator. In particular, there is
|
||||||
|
a library for string handling, memory management, array handling, math
|
||||||
|
(multiplication and division), and display routines to draw characters and
|
||||||
|
geometric forms.
|
||||||
|
|
||||||
|
I particularly enjoy seeing how much effort goes into simple routines like
|
||||||
|
multiplication and division. Of course, as a high-level programmer, you take
|
||||||
|
these operations for granted but getting into the details of how to implement
|
||||||
|
them is rewarding. I still learned a lot, even for this last project.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Nand to Tetris is one of my favorite classes of all time. I highly recommend it
|
||||||
|
to everybody who works in computer science and feels like they only partially
|
||||||
|
understand how a computer works. I certainly got many new insights throughout
|
||||||
|
the course, and I learned a new programming language, Rust, which I should use
|
||||||
|
more often over Python.
|
||||||
|
|
||||||
|
|||||||
BIN
gifs/automaton_drawing.png
Normal file
BIN
gifs/automaton_drawing.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
gifs/automaton_menu.png
Normal file
BIN
gifs/automaton_menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
BIN
gifs/compiler_loc.png
Normal file
BIN
gifs/compiler_loc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
gifs/vim_jack_syntax.png
Normal file
BIN
gifs/vim_jack_syntax.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
78
vim/jack.vim
Normal file
78
vim/jack.vim
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
if exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
syntax match jackError "\v^\s*[A-Za-z_]+"
|
||||||
|
|
||||||
|
syntax match jackIdentifier "\v<Math>"
|
||||||
|
syntax match jackIdentifier "\v<String>"
|
||||||
|
syntax match jackIdentifier "\v<Array>"
|
||||||
|
syntax match jackIdentifier "\v<Output>"
|
||||||
|
syntax match jackIdentifier "\v<Screen>"
|
||||||
|
syntax match jackIdentifier "\v<Keyboard>"
|
||||||
|
syntax match jackIdentifier "\v<Memory>"
|
||||||
|
syntax match jackIdentifier "\v<Sys>"
|
||||||
|
|
||||||
|
syntax match jackKeyword "\v\s*<class>"
|
||||||
|
syntax match jackFunction "\v\s*<constructor>"
|
||||||
|
syntax match jackFunction "\v\s*<method>"
|
||||||
|
syntax match jackFunction "\v\s*<function>"
|
||||||
|
|
||||||
|
syntax match jackType "\v<int>"
|
||||||
|
syntax match jackType "\v<boolean>"
|
||||||
|
syntax match jackType "\v<char>"
|
||||||
|
syntax match jackType "\v<void>"
|
||||||
|
|
||||||
|
syntax match jackKeyword "\v\s*<var>"
|
||||||
|
syntax match jackKeyword "\v\s*<static>"
|
||||||
|
syntax match jackKeyword "\v\s*<field>"
|
||||||
|
|
||||||
|
syntax match jackConditional "\v\s*<let>"
|
||||||
|
syntax match jackConditional "\v\s*<do>"
|
||||||
|
syntax match jackConditional "\v\s*<if>"
|
||||||
|
syntax match jackConditional "\v\s*<else>"
|
||||||
|
syntax match jackConditional "\v\s*<while>"
|
||||||
|
syntax match jackConditional "\v\s*<return>"
|
||||||
|
|
||||||
|
syntax match jackConstant "\v<true>"
|
||||||
|
syntax match jackConstant "\v<false>"
|
||||||
|
syntax match jackConstant "\v<null>"
|
||||||
|
|
||||||
|
syntax match jackKeyword "\v<this>"
|
||||||
|
|
||||||
|
syntax match jackOperator "\v\+"
|
||||||
|
syntax match jackOperator "\v-"
|
||||||
|
syntax match jackOperator "\v\*"
|
||||||
|
syntax match jackOperator "\v/"
|
||||||
|
syntax match jackOperator "\v\&"
|
||||||
|
syntax match jackOperator "\v\|"
|
||||||
|
syntax match jackOperator "\v\~"
|
||||||
|
syntax match jackOperator "\v\<"
|
||||||
|
syntax match jackOperator "\v\>"
|
||||||
|
|
||||||
|
syntax match jackNumber "\v<([1-9][0-9]*|[0-9])>"
|
||||||
|
syntax region jackString start=/\v"/ skip=/\v\\./ end=/\v"/
|
||||||
|
|
||||||
|
syntax match jackTodo "\vTODO:?" contained
|
||||||
|
syntax match jackTodo "\vFIXME:?" contained
|
||||||
|
syntax match jackTodo "\vXXX:?" contained
|
||||||
|
|
||||||
|
syntax match jackComment "\v//.*$" contains=jackTodo
|
||||||
|
syntax region jackComment start="\v/\*\*" end="\v\*/" contains=jackTodo
|
||||||
|
syntax region jackComment start="\v/\*" end="\v\*/" contains=jackTodo
|
||||||
|
|
||||||
|
highlight link jackError Error
|
||||||
|
highlight link jackIdentifier Identifier
|
||||||
|
highlight link jackString String
|
||||||
|
highlight link jackComment Comment
|
||||||
|
highlight link jackTodo Todo
|
||||||
|
highlight link jackFunction Function
|
||||||
|
highlight link jackConditional Conditional
|
||||||
|
highlight link jackType Type
|
||||||
|
highlight link jackConstant Constant
|
||||||
|
highlight link jackOperator Operator
|
||||||
|
highlight link jackKeyword Keyword
|
||||||
|
highlight link jackString String
|
||||||
|
highlight link jackNumber Number
|
||||||
|
|
||||||
|
let b:current_syntax = "jack"
|
||||||
Reference in New Issue
Block a user