4 changed files with 204 additions and 1 deletions
@ -0,0 +1,100 @@ |
|||||||
|
{-# LANGUAGE OverloadedStrings #-} |
||||||
|
|
||||||
|
module Nand2Tetris.VM |
||||||
|
( |
||||||
|
VMInstruction(..), |
||||||
|
ArithmeticCmd(..), |
||||||
|
AccessType(..), |
||||||
|
SegmentOffset(..), |
||||||
|
SegmentId(..), |
||||||
|
parseInstruction |
||||||
|
) where |
||||||
|
|
||||||
|
import Nand2Tetris.Error ( Error ) |
||||||
|
import Text.Megaparsec (Parsec, MonadParsec (try), (<|>), choice) |
||||||
|
import qualified Data.Text as T |
||||||
|
import Text.Megaparsec.Char (string, space1) |
||||||
|
import Data.Functor (($>)) |
||||||
|
import qualified Text.Megaparsec.Char.Lexer as L |
||||||
|
|
||||||
|
data VMInstruction = |
||||||
|
VMArithmetic ArithmeticCmd |
||||||
|
| VMMemAccess AccessType SegmentId SegmentOffset |
||||||
|
deriving (Show, Eq) |
||||||
|
|
||||||
|
data ArithmeticCmd = |
||||||
|
ACAdd |
||||||
|
| ACSub |
||||||
|
| ACNeg |
||||||
|
| ACEq |
||||||
|
| ACGt |
||||||
|
| ACLt |
||||||
|
| ACAnd |
||||||
|
| ACOr |
||||||
|
| ACNot |
||||||
|
deriving (Show, Eq) |
||||||
|
|
||||||
|
data AccessType = |
||||||
|
ACPush |
||||||
|
| ACPop |
||||||
|
deriving (Show, Eq) |
||||||
|
|
||||||
|
newtype SegmentOffset = SegmentOffset { unSegmentOffset :: Int } |
||||||
|
deriving (Show, Eq) |
||||||
|
|
||||||
|
data SegmentId = |
||||||
|
SLocal |
||||||
|
| SArgument |
||||||
|
| SThis |
||||||
|
| SThat |
||||||
|
| SConstant |
||||||
|
| SStatic |
||||||
|
| SPointer |
||||||
|
| STemp |
||||||
|
deriving (Show, Eq) |
||||||
|
|
||||||
|
type Parser = Parsec Error T.Text |
||||||
|
|
||||||
|
parseInstruction :: Parser VMInstruction |
||||||
|
parseInstruction = |
||||||
|
try parseMemAccess <|> |
||||||
|
parseArithmetic |
||||||
|
|
||||||
|
where |
||||||
|
parseMemAccess :: Parser VMInstruction |
||||||
|
parseMemAccess = VMMemAccess <$> |
||||||
|
parseAccessType <*> |
||||||
|
parseSegmentId <*> |
||||||
|
parseSegmentOffset |
||||||
|
|
||||||
|
parseAccessType :: Parser AccessType |
||||||
|
parseAccessType = try (string "push" >> space1 $> ACPush) <|> |
||||||
|
(string "pop" >> space1 $> ACPop) |
||||||
|
|
||||||
|
parseSegmentId :: Parser SegmentId |
||||||
|
parseSegmentId = |
||||||
|
choice [ string "local" $> SLocal, |
||||||
|
string "argument" $> SArgument, |
||||||
|
string "this" $> SThis, |
||||||
|
string "that" $> SThat, |
||||||
|
string "constant" $> SConstant, |
||||||
|
string "static" $> SStatic, |
||||||
|
string "pointer" $> SPointer, |
||||||
|
string "temp" $> STemp |
||||||
|
] <* space1 |
||||||
|
|
||||||
|
parseSegmentOffset :: Parser SegmentOffset |
||||||
|
parseSegmentOffset = SegmentOffset <$> L.decimal |
||||||
|
|
||||||
|
parseArithmetic :: Parser VMInstruction |
||||||
|
parseArithmetic = VMArithmetic <$> |
||||||
|
choice [ string "add" $> ACAdd, |
||||||
|
string "sub" $> ACSub, |
||||||
|
string "neg" $> ACNeg, |
||||||
|
string "eq" $> ACEq, |
||||||
|
string "gt" $> ACGt, |
||||||
|
string "lt" $> ACLt, |
||||||
|
string "and" $> ACAnd, |
||||||
|
string "or" $> ACOr, |
||||||
|
string "not" $> ACNot |
||||||
|
] |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
|
||||||
|
module Main (main) where |
||||||
|
import Test.Hspec (hspec, Spec, describe) |
||||||
|
|
||||||
|
import qualified Test.Nand2Tetris.VM as VM |
||||||
|
|
||||||
|
main = hspec spec |
||||||
|
|
||||||
|
spec :: Spec |
||||||
|
spec = do |
||||||
|
describe "VM" VM.spec |
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,79 @@ |
|||||||
|
{-# LANGUAGE OverloadedStrings #-} |
||||||
|
|
||||||
|
module Test.Nand2Tetris.VM |
||||||
|
( |
||||||
|
spec |
||||||
|
) where |
||||||
|
|
||||||
|
import Test.Hspec (Spec, describe, it, shouldBe) |
||||||
|
|
||||||
|
import Nand2Tetris.VM (VMInstruction(VMMemAccess, VMArithmetic), |
||||||
|
SegmentOffset(SegmentOffset), |
||||||
|
AccessType(ACPush, ACPop), |
||||||
|
SegmentId(SArgument, SLocal, SThis, SThat, SConstant, STemp, SPointer, SStatic), |
||||||
|
SegmentOffset(SegmentOffset), |
||||||
|
parseInstruction, ArithmeticCmd (ACAdd, ACNeg, ACSub, ACEq, ACGt, ACLt, ACAnd, ACOr, ACNot)) |
||||||
|
import Text.Megaparsec (runParser) |
||||||
|
|
||||||
|
spec :: Spec |
||||||
|
spec = do |
||||||
|
describe "parseInstruction" $ do |
||||||
|
it "Parses instruction: push argument 42" $ |
||||||
|
doParse "push argument 42" `shouldBe` Right (VMMemAccess ACPush SArgument (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: pop argument 42" $ |
||||||
|
doParse "pop argument 42" `shouldBe` Right (VMMemAccess ACPop SArgument (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push local 42" $ |
||||||
|
doParse "push local 42" `shouldBe` Right (VMMemAccess ACPush SLocal (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push this 42" $ |
||||||
|
doParse "push this 42" `shouldBe` Right (VMMemAccess ACPush SThis (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push that 42" $ |
||||||
|
doParse "push that 42" `shouldBe` Right (VMMemAccess ACPush SThat (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push constant 42" $ |
||||||
|
doParse "push constant 42" `shouldBe` Right (VMMemAccess ACPush SConstant (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push static 42" $ |
||||||
|
doParse "push static 42" `shouldBe` Right (VMMemAccess ACPush SStatic (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push pointer 42" $ |
||||||
|
doParse "push pointer 42" `shouldBe` Right (VMMemAccess ACPush SPointer (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push temp 42" $ |
||||||
|
doParse "push temp 42" `shouldBe` Right (VMMemAccess ACPush STemp (SegmentOffset 42)) |
||||||
|
|
||||||
|
it "Parses instruction: push temp 11" $ |
||||||
|
doParse "push temp 11" `shouldBe` Right (VMMemAccess ACPush STemp (SegmentOffset 11)) |
||||||
|
|
||||||
|
it "Parses instruction: add" $ |
||||||
|
doParse "add" `shouldBe` Right (VMArithmetic ACAdd) |
||||||
|
|
||||||
|
it "Parses instruction: sub" $ |
||||||
|
doParse "sub" `shouldBe` Right (VMArithmetic ACSub) |
||||||
|
|
||||||
|
it "Parses instruction: neg" $ |
||||||
|
doParse "neg" `shouldBe` Right (VMArithmetic ACNeg) |
||||||
|
|
||||||
|
it "Parses instruction: eq" $ |
||||||
|
doParse "eq" `shouldBe` Right (VMArithmetic ACEq) |
||||||
|
|
||||||
|
it "Parses instruction: gt" $ |
||||||
|
doParse "gt" `shouldBe` Right (VMArithmetic ACGt) |
||||||
|
|
||||||
|
it "Parses instruction: lt" $ |
||||||
|
doParse "lt" `shouldBe` Right (VMArithmetic ACLt) |
||||||
|
|
||||||
|
it "Parses instruction: and" $ |
||||||
|
doParse "and" `shouldBe` Right (VMArithmetic ACAnd) |
||||||
|
|
||||||
|
it "Parses instruction: or" $ |
||||||
|
doParse "or" `shouldBe` Right (VMArithmetic ACOr) |
||||||
|
|
||||||
|
it "Parses instruction: not" $ |
||||||
|
doParse "not" `shouldBe` Right (VMArithmetic ACNot) |
||||||
|
|
||||||
|
where |
||||||
|
doParse = runParser parseInstruction "" |
||||||
Loading…
Reference in new issue