|
|
|
|
@ -14,7 +14,8 @@ import Nand2Tetris.VM (AccessType (ACPop, ACPush),
@@ -14,7 +14,8 @@ import Nand2Tetris.VM (AccessType (ACPop, ACPush),
|
|
|
|
|
ArithmeticCmd (ACAdd, ACAnd, ACEq, ACGt, ACLt, ACNeg, ACNot, ACOr, ACSub), |
|
|
|
|
Segment (SArgument, SConstant, SLocal, SPointer, SStatic, STemp, SThat, SThis), |
|
|
|
|
VMInstruction (VMArithmetic, VMMemAccess), |
|
|
|
|
compileInstruction, parseInstruction) |
|
|
|
|
compileInstruction, parseInstruction, |
|
|
|
|
runCompilerDef) |
|
|
|
|
import Text.Megaparsec (runParser) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -23,6 +24,7 @@ spec = do
@@ -23,6 +24,7 @@ spec = do
|
|
|
|
|
describe "parseInstruction" parseInstructionSpec |
|
|
|
|
describe "compile push instruction" compilePushInstructionSpec |
|
|
|
|
describe "compile pop instruction" compilePopInstructionSpec |
|
|
|
|
describe "compile arithmetic instrucitons" compileArithInstructionSpec |
|
|
|
|
|
|
|
|
|
parseInstructionSpec :: Spec |
|
|
|
|
parseInstructionSpec = do |
|
|
|
|
@ -89,27 +91,27 @@ parseInstructionSpec = do
@@ -89,27 +91,27 @@ parseInstructionSpec = do
|
|
|
|
|
compilePushInstructionSpec :: Spec |
|
|
|
|
compilePushInstructionSpec = do |
|
|
|
|
it "Compiles push argument 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SArgument 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SArgument 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "push argument 42") |
|
|
|
|
tail compiled `shouldBe` pushValue "ARG" 42 |
|
|
|
|
|
|
|
|
|
it "Compiles push local 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SLocal 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SLocal 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "push local 42") |
|
|
|
|
tail compiled `shouldBe` pushValue "LCL" 42 |
|
|
|
|
|
|
|
|
|
it "Compiles push this 41" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SThis 41)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SThis 41)) |
|
|
|
|
head compiled `shouldBe` (Comment "push this 41") |
|
|
|
|
tail compiled `shouldBe` pushValue "THIS" 41 |
|
|
|
|
|
|
|
|
|
it "Compiles push that 41" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SThat 41)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SThat 41)) |
|
|
|
|
head compiled `shouldBe` (Comment "push that 41") |
|
|
|
|
tail compiled `shouldBe` pushValue "THAT" 41 |
|
|
|
|
|
|
|
|
|
it "Compiles push constant 17" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SConstant 17)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SConstant 17)) |
|
|
|
|
head compiled `shouldBe` (Comment "push constant 17") |
|
|
|
|
tail compiled `shouldBe` [ |
|
|
|
|
Code (A (Imm 17)), |
|
|
|
|
@ -122,22 +124,22 @@ compilePushInstructionSpec = do
@@ -122,22 +124,22 @@ compilePushInstructionSpec = do
|
|
|
|
|
Code (C [RegM] (SReg RegD) JNone) ] |
|
|
|
|
|
|
|
|
|
it "Compiles push static Foo.1" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SStatic "Foo.1")) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SStatic "Foo.1")) |
|
|
|
|
head compiled `shouldBe` (Comment "push static Foo.1") |
|
|
|
|
tail compiled `shouldBe` pushLabel "Foo.1" |
|
|
|
|
|
|
|
|
|
it "Compiles push pointer 0" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SPointer 0)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SPointer 0)) |
|
|
|
|
head compiled `shouldBe` (Comment "push pointer 0") |
|
|
|
|
tail compiled `shouldBe` pushLabel "THIS" |
|
|
|
|
|
|
|
|
|
it "Compiles push pointer 1" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (SPointer 1)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (SPointer 1)) |
|
|
|
|
head compiled `shouldBe` (Comment "push pointer 1") |
|
|
|
|
tail compiled `shouldBe` pushLabel "THAT" |
|
|
|
|
|
|
|
|
|
it "Compiles push temp 2" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPush (STemp 2)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPush (STemp 2)) |
|
|
|
|
head compiled `shouldBe` (Comment "push temp 2") |
|
|
|
|
tail compiled `shouldBe` pushRam (5 + 2) |
|
|
|
|
|
|
|
|
|
@ -177,42 +179,42 @@ compilePushInstructionSpec = do
@@ -177,42 +179,42 @@ compilePushInstructionSpec = do
|
|
|
|
|
compilePopInstructionSpec :: Spec |
|
|
|
|
compilePopInstructionSpec = do |
|
|
|
|
it "pop argument 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SArgument 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SArgument 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop argument 42") |
|
|
|
|
tail compiled `shouldBe` popValue "ARG" 42 |
|
|
|
|
|
|
|
|
|
it "pop local 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SLocal 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SLocal 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop local 42") |
|
|
|
|
tail compiled `shouldBe` popValue "LCL" 42 |
|
|
|
|
|
|
|
|
|
it "pop this 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SThis 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SThis 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop this 42") |
|
|
|
|
tail compiled `shouldBe` popValue "THIS" 42 |
|
|
|
|
|
|
|
|
|
it "pop that 42" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SThat 42)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SThat 42)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop that 42") |
|
|
|
|
tail compiled `shouldBe` popValue "THAT" 42 |
|
|
|
|
|
|
|
|
|
it "pop static Foo.1" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SStatic "Foo.1")) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SStatic "Foo.1")) |
|
|
|
|
head compiled `shouldBe` (Comment "pop static Foo.1") |
|
|
|
|
tail compiled `shouldBe` popLabelValue "Foo.1" |
|
|
|
|
|
|
|
|
|
it "pop pointer 0" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SPointer 0)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SPointer 0)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop pointer 0") |
|
|
|
|
tail compiled `shouldBe` popLabelValue "THIS" |
|
|
|
|
|
|
|
|
|
it "pop pointer 1" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (SPointer 1)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (SPointer 1)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop pointer 1") |
|
|
|
|
tail compiled `shouldBe` popLabelValue "THAT" |
|
|
|
|
|
|
|
|
|
it "pop temp 1" $ do |
|
|
|
|
let compiled = compileInstruction (VMMemAccess ACPop (STemp 1)) |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMMemAccess ACPop (STemp 1)) |
|
|
|
|
head compiled `shouldBe` (Comment "pop temp 1") |
|
|
|
|
tail compiled `shouldBe` popRam (5 + 1) |
|
|
|
|
|
|
|
|
|
@ -249,3 +251,46 @@ compilePopInstructionSpec = do
@@ -249,3 +251,46 @@ compilePopInstructionSpec = do
|
|
|
|
|
Code (A . Imm $ offset), |
|
|
|
|
Code (C [RegM] (SReg RegD) JNone) ] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
compileArithInstructionSpec :: Spec |
|
|
|
|
compileArithInstructionSpec = do |
|
|
|
|
it "compiles add" $ do |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMArithmetic ACAdd) |
|
|
|
|
head compiled `shouldBe` (Comment "add") |
|
|
|
|
tail compiled `shouldBe` |
|
|
|
|
[ |
|
|
|
|
Code (A (Label "SP")), |
|
|
|
|
Code (C [RegA] (SRegMinus1 RegM) JNone), |
|
|
|
|
Code (C [RegD] (SReg RegM) JNone), |
|
|
|
|
Code (C [RegA] (SRegMinus1 RegA) JNone), |
|
|
|
|
Code (C [RegM] SDPlusM JNone), |
|
|
|
|
Code (C [RegD] (SRegPlus1 RegA) JNone), |
|
|
|
|
Code (A (Label "SP")), |
|
|
|
|
Code (C [RegM] (SReg RegD) JNone) |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
it "compiles sub" $ do |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMArithmetic ACSub) |
|
|
|
|
head compiled `shouldBe` (Comment "sub") |
|
|
|
|
tail compiled `shouldBe` |
|
|
|
|
[ |
|
|
|
|
Code (A (Label "SP")), |
|
|
|
|
Code (C [RegA] (SRegMinus1 RegM) JNone), |
|
|
|
|
Code (C [RegD] (SReg RegM) JNone), |
|
|
|
|
Code (C [RegA] (SRegMinus1 RegA) JNone), |
|
|
|
|
Code (C [RegM] SMMinusD JNone), |
|
|
|
|
Code (C [RegD] (SRegPlus1 RegA) JNone), |
|
|
|
|
Code (A (Label "SP")), |
|
|
|
|
Code (C [RegM] (SReg RegD) JNone) |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
it "compiles neg" $ do |
|
|
|
|
let compiled = runCompilerDef $ compileInstruction (VMArithmetic ACNeg) |
|
|
|
|
head compiled `shouldBe` (Comment "neg") |
|
|
|
|
tail compiled `shouldBe` |
|
|
|
|
[ |
|
|
|
|
Code (A (Label "SP")), |
|
|
|
|
Code (C [RegA] (SRegMinus1 RegM) JNone), |
|
|
|
|
Code (C [RegM] (SNeg RegM) JNone) |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
|