Skip to content

Commit

Permalink
feat(vm): stack arithmetic
Browse files Browse the repository at this point in the history
  • Loading branch information
qazwsxedckll committed Feb 2, 2024
1 parent eb60233 commit fb42c3e
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 12 deletions.
113 changes: 111 additions & 2 deletions vmtranslator/code_writer.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package main

import "io"
import (
"bytes"
"io"
"strconv"
)

type CodeWriter struct {
fileName string
w io.WriteCloser
jump int
}

func NewCodeWriter(w io.WriteCloser) *CodeWriter {
Expand All @@ -18,9 +23,65 @@ func (c *CodeWriter) SetFileNmae(fileName string) {
}

func (c *CodeWriter) WriteArithmetic(command string) {
buffer := bytes.Buffer{}

switch command {
case "add":
popBinary(&buffer)
buffer.WriteString(`M=M+D`)
case "sub":
popBinary(&buffer)
buffer.WriteString(`M=M-D`)
case "neg":
popBinary(&buffer)
buffer.WriteString(`M=-M`)
case "eq":
c.jump = writeCompare(&buffer, c.jump, "JEQ")
case "gt":
c.jump = writeCompare(&buffer, c.jump, "JGT")
case "lt":
c.jump = writeCompare(&buffer, c.jump, "JLT")
case "and":
popBinary(&buffer)
buffer.WriteString(`M=M&D`)
case "or":
popBinary(&buffer)
buffer.WriteString(`M=M|D`)
case "not":
popUnary(&buffer)
buffer.WriteString(`M=!M`)
}

_, err := c.w.Write(buffer.Bytes())
if err != nil {
panic(err)
}
}

func (c *CodeWriter) WritePushPop(command string, segment string, index int) {
func (c *CodeWriter) WritePushPop(command CommandType, segment string, index int) {
buffer := bytes.Buffer{}
switch command {
case "push":
switch segment {
case "constant":
buffer.WriteString(`@` + strconv.Itoa(index))
buffer.WriteString(`D=A`)
push(&buffer)
case "local":
case "argument":
case "this":
case "that":
case "temp":
case "pointer":
case "static":
}
case "pop":
}

_, err := c.w.Write(buffer.Bytes())
if err != nil {
panic(err)
}
}

func (c *CodeWriter) Close() {
Expand All @@ -29,3 +90,51 @@ func (c *CodeWriter) Close() {
panic(err)
}
}

func writeFalse(buf *bytes.Buffer) {
buf.WriteString(`@SP`)
buf.WriteString(`A=M-1`)
buf.WriteString(`M=0`)
}

func writeTrue(buf *bytes.Buffer) {
buf.WriteString(`@SP`)
buf.WriteString(`A=M-1`)
buf.WriteString(`M=-1`)
}

func writeCompare(buffer *bytes.Buffer, jump int, compare string) int {
popBinary(buffer)
buffer.WriteString(`D=M-D`)
buffer.WriteString(`@JUMP_` + strconv.Itoa(jump))
buffer.WriteString(`D;` + compare)
writeFalse(buffer)
buffer.WriteString(`@END_` + strconv.Itoa(jump))
buffer.WriteString(`0;JMP`)
buffer.WriteString(`(JUMP_` + strconv.Itoa(jump) + `)`)
writeTrue(buffer)
buffer.WriteString(`(END_` + strconv.Itoa(jump) + `)`)
return jump + 1
}

// one value in D, one value in M
func popBinary(buf *bytes.Buffer) {
buf.WriteString(`@SP`)
buf.WriteString(`AM=M-1`)
buf.WriteString(`D=M`)
buf.WriteString(`A=A-1`)
}

// one value in M
func popUnary(buf *bytes.Buffer) {
buf.WriteString(`@SP`)
buf.WriteString(`A=M-1`)
}

func push(buf *bytes.Buffer) {
buf.WriteString(`@SP`)
buf.WriteString(`A=M`)
buf.WriteString(`M=D`)
buf.WriteString(`@SP`)
buf.WriteString(`M=M+1`)
}
46 changes: 44 additions & 2 deletions vmtranslator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"io"
"os"
"strings"
)
Expand All @@ -22,12 +23,53 @@ func main() {

for _, file := range files {
if strings.HasSuffix(file.Name(), ".vm") {
// TODO: Process .vm file and write to .asm file
in, err := os.Open(arg + "/" + file.Name())
if err != nil {
panic(err)
}
out, err := os.OpenFile(file.Name()[:len(file.Name())-3]+".asm", os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
panic(err)
}
parse(in, out, out.Name())
}
}
} else if strings.HasSuffix(arg, ".vm") {
// TODO: Process .vm file and write to .asm file
in, err := os.Open(arg)
if err != nil {
panic(err)
}
out, err := os.OpenFile(arg[:len(arg)-3]+".asm", os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
panic(err)
}
parse(in, out, out.Name())
} else {
panic("Invalid argument. Please provide a .vm file or a directory.")
}
}

func parse(in io.Reader, out io.WriteCloser, filename string) {
codeWriter := NewCodeWriter(out)
codeWriter.SetFileNmae(filename)
defer codeWriter.w.Close()

parser, err := NewParser(in)
if err != nil {
panic(err)
}

for parser.HasMoreCommands() {
parser.Advance()

cmdType := parser.CommandType()
switch cmdType {
case C_ARITHMETIC:
codeWriter.WriteArithmetic(parser.Arg1())
case C_PUSH, C_POP:
codeWriter.WritePushPop(cmdType, parser.Arg1(), parser.Arg2())
default:
panic(fmt.Sprintf("unknown command type: %v", cmdType))
}
}
}
59 changes: 51 additions & 8 deletions vmtranslator/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,36 @@ package main

import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
)

type CommandType string
type (
CommandType string
ArithmeticCommand string
)

var cmdMap = map[string]CommandType{
"add": C_ARITHMETIC,
"sub": C_ARITHMETIC,
"neg": C_ARITHMETIC,
"eq": C_ARITHMETIC,
"gt": C_ARITHMETIC,
"lt": C_ARITHMETIC,
"and": C_ARITHMETIC,
"or": C_ARITHMETIC,
"not": C_ARITHMETIC,
"push": C_PUSH,
"pop": C_POP,
"label": C_LABEL,
"goto": C_GOTO,
"if-goto": C_IF,
"function": C_FUNCTION,
"return": C_RETURN,
"call": C_CALL,
}

const (
C_ARITHMETIC CommandType = "C_ARITHMETIC"
Expand Down Expand Up @@ -65,17 +90,35 @@ func (p *Parser) Advance() {
}

func (p *Parser) CommandType() CommandType {
// if strings.HasPrefix(p.commands[p.current], "@") {
// return ACommand
// } else if strings.HasPrefix(p.commands[p.current], "(") {
// return LCommand
// } else {
// return CCommand
// }
cmd := strings.Fields(p.commands[p.current])[0]
if c, ok := cmdMap[cmd]; ok {
return c
}

panic(fmt.Sprintf("unknown command: %s", p.commands[p.current]))
}

func (p *Parser) Arg1() string {
if p.CommandType() == C_RETURN {
panic("no argument for return command")
}

if p.CommandType() == C_ARITHMETIC {
return p.commands[p.current]
}

return strings.Fields(p.commands[p.current])[1]
}

func (p *Parser) Arg2() int {
if p.CommandType() == C_PUSH || p.CommandType() == C_POP || p.CommandType() == C_FUNCTION || p.CommandType() == C_CALL {
i, err := strconv.Atoi(strings.Fields(p.commands[p.current])[2])
if err != nil {
panic(err)
}

return i
}

panic("no second argument for command")
}

0 comments on commit fb42c3e

Please sign in to comment.