Skip to content

Commit

Permalink
Add Assembler of WhiteSpace
Browse files Browse the repository at this point in the history
  • Loading branch information
kamil-adam committed Jan 14, 2021
1 parent 9403517 commit 9dc649f
Show file tree
Hide file tree
Showing 29 changed files with 1,191 additions and 51 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ cabal.project.local~

Gemfile.lock
_site

.hlint-*/
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Revision history for helpa

## 0.3.0.0 -- 2021-01-14

* Add WSA, Assembler of WhiteSpace

## 0.2.0.0 -- 2020-08-09

* Add EAS, Assembler of ETA
Expand Down
2 changes: 1 addition & 1 deletion docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Features to do:
* [ ] 💛 Funge Assembler
* [ ] 💚 Piet Assembler
* [ ] 💙 SubLeq Assembler
* [ ] 🤍 WhiteSpace Assembler
* [x] 🤍 WhiteSpace Assembler
* [ ] 🖤 WMachine Assembler

You can propose feature by [GitHub](https://github.com/helvm/helap/issues).
Expand Down
24 changes: 24 additions & 0 deletions helpa.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ library
HelVM.HelPA.Assemblers.EAS.Linker
HelVM.HelPA.Assemblers.EAS.Reducer

HelVM.HelPA.Assemblers.WSA.AsmParser
HelVM.HelPA.Assemblers.WSA.Assembler
HelVM.HelPA.Assemblers.WSA.CodeGenerator
HelVM.HelPA.Assemblers.WSA.Instruction
HelVM.HelPA.Assemblers.WSA.Linker
HelVM.HelPA.Assemblers.WSA.Reducer
HelVM.HelPA.Assemblers.WSA.Token

HelVM.HelPA.Common.AsmParserUtil
HelVM.HelPA.Common.OrError
HelVM.HelPA.Common.Util
Expand All @@ -44,6 +52,7 @@ library
, attoparsec
, containers
, mtl
, sort
, split
, text
hs-source-dirs: src/main/eta
Expand All @@ -61,12 +70,19 @@ test-suite spec
HelVM.HelPA.Assemblers.EAS.TestData
HelVM.HelPA.Assemblers.EAS.ReducerSpec

HelVM.HelPA.Assemblers.WSA.AsmParserSpec
HelVM.HelPA.Assemblers.WSA.AssemblerSpec
HelVM.HelPA.Assemblers.WSA.CodeGeneratorSpec
HelVM.HelPA.Assemblers.WSA.LinkerSpec
HelVM.HelPA.Assemblers.WSA.ReducerSpec
HelVM.HelPA.Assemblers.WSA.TestData
other-extensions:
build-depends:
base
, attoparsec
, containers
, mtl
, sort
, split
, text
, helpa
Expand All @@ -91,6 +107,14 @@ executable helpa
HelVM.HelPA.Assemblers.EAS.Linker
HelVM.HelPA.Assemblers.EAS.Reducer

HelVM.HelPA.Assemblers.WSA.AsmParser
HelVM.HelPA.Assemblers.WSA.Assembler
HelVM.HelPA.Assemblers.WSA.CodeGenerator
HelVM.HelPA.Assemblers.WSA.Instruction
HelVM.HelPA.Assemblers.WSA.Linker
HelVM.HelPA.Assemblers.WSA.Reducer
HelVM.HelPA.Assemblers.WSA.Token

HelVM.HelPA.Common.AsmParserUtil
HelVM.HelPA.Common.OrError
HelVM.HelPA.Common.Util
Expand Down
10 changes: 8 additions & 2 deletions src/main/eta/AppOptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,29 @@ optionParser = AppOptions
<> value (show HAPAPL)
<> showDefault
)
<*> switch ( long "debug"
<> short 'D'
<> help "Debug"
<> showDefault
)
<*> argument str ( metavar "DIR")
<*> argument str ( metavar "FILE")


data AppOptions = AppOptions
{ lang :: String --Lang
, debug :: Bool
, dir :: String
, file :: String
}

----

data Lang = HAPAPL | EAS
data Lang = HAPAPL | EAS | WSA
deriving (Eq, Read, Show)

langs :: [Lang]
langs = [HAPAPL, EAS]
langs = [HAPAPL, EAS, WSA]

computeLang :: String -> Lang
computeLang raw = valid $ readMaybe raw where
Expand Down
4 changes: 2 additions & 2 deletions src/main/eta/HelVM/HelPA/Assemblers/EAS/Assembler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import Control.Monad.Except
import qualified Data.Text as T

assemblyIO :: String -> String -> IO (Either String String)
assemblyIO dirName fileName = runExceptT $ assembly dirName fileName
assemblyIO dirPath filePath = runExceptT $ assembly dirPath filePath

assembly :: String -> String -> ExceptT String IO String
assembly dirName fileName = generateCode . reduce <$> link dirName fileName
assembly dirPath filePath = generateCode . reduce <$> link dirPath filePath

singleAssembly :: T.Text -> Either String String
singleAssembly t = generateCode . reduce <$> parseAssembler t
23 changes: 13 additions & 10 deletions src/main/eta/HelVM/HelPA/Assemblers/EAS/Linker.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import Control.Monad.Except

import qualified Data.Text.IO as T

linkLibIO :: String -> String -> IO (Either String InstructionList)
linkLibIO dirPath fileName = runExceptT $ linkLib dirPath fileName

linkIO :: String -> String -> IO (Either String InstructionList)
linkIO dirName fileName = runExceptT $ link dirName fileName
linkIO dirPath filePath = runExceptT $ link dirPath filePath

link :: String -> String -> ExceptT String IO InstructionList
link dirName fileName = includeFiles $ ExceptT $ parseAssembler <$> T.readFile (dirName ++ "/" ++ fileName) where
linkLib :: String -> String -> ExceptT String IO InstructionList
linkLib dirPath fileName = link dirPath $ dirPath ++ "/" ++ fileName

includeFiles :: ExceptT String IO InstructionList -> ExceptT String IO InstructionList
includeFiles expect = loadFiles =<< expect
link :: String -> String -> ExceptT String IO InstructionList
link dirPath filePath = (includeLibs dirPath =<<) $ ExceptT $ parseAssembler <$> T.readFile filePath

loadFiles :: InstructionList -> ExceptT String IO InstructionList
loadFiles il = concat <$> mapM loadFile il
includeLibs :: String -> InstructionList -> ExceptT String IO InstructionList
includeLibs dirPath il = concat <$> mapM (includeLib dirPath) il

loadFile :: Instruction -> ExceptT String IO InstructionList
loadFile (D libName) = link dirName libName
loadFile i = pure [i]
includeLib :: String -> Instruction -> ExceptT String IO InstructionList
includeLib dirPath (D libName) = linkLib dirPath libName
includeLib _ i = pure [i]
96 changes: 96 additions & 0 deletions src/main/eta/HelVM/HelPA/Assemblers/WSA/AsmParser.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{-# LANGUAGE OverloadedStrings #-}
module HelVM.HelPA.Assemblers.WSA.AsmParser (parseAssembler) where

import HelVM.HelPA.Assemblers.WSA.Instruction

import HelVM.HelPA.Common.AsmParserUtil
import HelVM.HelPA.Common.Value

import Control.Applicative
import Data.Attoparsec.Text hiding (I, D)
import Data.Char
import Data.Maybe

import qualified Data.Text as T

parseAssembler :: T.Text -> Either String InstructionList
parseAssembler = parseOnly instructionListParser

instructionListParser :: Parser InstructionList
instructionListParser = catMaybes <$> many maybeInstructionParser

maybeInstructionParser :: Parser (Maybe Instruction)
maybeInstructionParser =
Just <$> (skipSpace *> instructionParser <* skipHorizontalSpace <* optional skipComment)
<|> (Nothing <$ (skipSpace *> skipComment))

----

instructionParser :: Parser Instruction
instructionParser =
try pushSParser
<|> maybeOperandInstructionParser
<|> identifierOperandInstructionParser
<|> zeroOperandInstructionParser
<|> pushParser
<|> testParser

zeroOperandInstructionParser :: Parser Instruction
zeroOperandInstructionParser =
parser Pop "pop"
<|> parser Dup "doub"
<|> parser Swap "swap"
<|> parser Return "ret"
<|> parser End "exit"
<|> parser OutputNum "outn"
<|> parser OutputChar "outc"
<|> parser InputNum "inn"
<|> parser InputChar "inc"
where parser i t = i <$ (asciiCI t *> endWordParser)

maybeOperandInstructionParser :: Parser Instruction
maybeOperandInstructionParser =
parser Add "add"
<|> parser Sub "sub"
<|> parser Mul "mul"
<|> parser Div "div"
<|> parser Mod "mod"
<|> parser Store "store"
<|> parser Load "retrive"
where parser f t = f <$> (asciiCI t *> optional (Literal <$> (skip1HorizontalSpace *> integerParser)))

identifierOperandInstructionParser :: Parser Instruction
identifierOperandInstructionParser =
parser Mark "label"
<|> parser Call "call"
<|> parser Branch "jump"
<|> parser BranchZ "jumpZ"
<|> parser BranchN "jumpN"
<|> parser BranchP "jumpP"
<|> parser BranchNZ "jumpNZ"
<|> parser BranchPZ "jumpPZ"
<|> parser Include "include"
where parser f t = f <$> (asciiCI t *> skip1HorizontalSpace *> identifierParser)

testParser :: Parser Instruction
testParser = Test <$> (asciiCI "test" *> skipHorizontalSpace *> integerParser)

pushParser :: Parser Instruction
pushParser = Push . Literal <$> (asciiCI "push" *> skip1HorizontalSpace *> integerParser)

pushSParser :: Parser Instruction
pushSParser = PushS . Literal <$> (asciiCI "pushs" *> skipHorizontalSpace *> stringParser)

----

skipComment :: Parser ()
skipComment = char commentChar *> skipAllToEndOfLine

endWordParser :: Parser T.Text
endWordParser = takeTill isEndWord

isEndWord :: Char -> Bool
isEndWord c = isSpace c || (commentChar == c)

commentChar :: Char
commentChar = ';'
19 changes: 19 additions & 0 deletions src/main/eta/HelVM/HelPA/Assemblers/WSA/Assembler.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module HelVM.HelPA.Assemblers.WSA.Assembler where

import HelVM.HelPA.Assemblers.WSA.AsmParser
import HelVM.HelPA.Assemblers.WSA.CodeGenerator
import HelVM.HelPA.Assemblers.WSA.Linker
import HelVM.HelPA.Assemblers.WSA.Reducer

import Control.Monad.Except

import qualified Data.Text as T

assemblyIO :: Bool -> String -> String -> IO (Either String String)
assemblyIO debug dirPath filePath = runExceptT $ assembly debug dirPath filePath

assembly :: Bool -> String -> String -> ExceptT String IO String
assembly debug dirPath filePath = generateCode debug . reduce <$> link dirPath filePath

singleAssembly :: Bool -> T.Text -> Either String String
singleAssembly debug code = generateCode debug . reduce <$> parseAssembler code
86 changes: 86 additions & 0 deletions src/main/eta/HelVM/HelPA/Assemblers/WSA/CodeGenerator.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
module HelVM.HelPA.Assemblers.WSA.CodeGenerator where

import HelVM.HelPA.Assemblers.WSA.Instruction
import HelVM.HelPA.Assemblers.WSA.Token

import HelVM.HelPA.Common.Util
import HelVM.HelPA.Common.Value

import Data.Char
import Numeric.Natural

generateCode :: Bool -> InstructionList -> String
generateCode debug il = showTL $ generateTL debug il

showTL :: TokenList -> String
showTL tl = show =<< tl

generateTL :: Bool -> InstructionList -> TokenList
generateTL debug il = generateTLForInstruction debug =<< il

generateTLForInstruction :: Bool -> Instruction -> TokenList
-- Stack instructions
generateTLForInstruction _ (Push (Literal value)) = [S,S] ++ integerToTL value
generateTLForInstruction _ Pop = [S,S,N]
generateTLForInstruction _ Dup = [S,N,S]
generateTLForInstruction _ Swap = [S,N,T]
--Arithmetic
generateTLForInstruction _ (Add Nothing) = [T,S,S,S]
generateTLForInstruction _ (Sub Nothing) = [T,S,S,T]
generateTLForInstruction _ (Mul Nothing) = [T,S,S,N]
generateTLForInstruction _ (Div Nothing) = [T,S,T,S]
generateTLForInstruction _ (Mod Nothing) = [T,S,T,T]
-- Heap access
generateTLForInstruction _ (Store Nothing) = [T,T,S]
generateTLForInstruction _ (Load Nothing) = [T,T,T]
-- Control
generateTLForInstruction _ (Mark label) = [N,S,S] ++ stringToTL label
generateTLForInstruction _ (Call label) = [N,S,T] ++ stringToTL label
generateTLForInstruction _ (Branch label) = [N,S,N] ++ stringToTL label
generateTLForInstruction _ (BranchZ label) = [N,T,S] ++ stringToTL label
generateTLForInstruction _ (BranchN label) = [N,T,T] ++ stringToTL label
generateTLForInstruction _ Return = [N,T,N]
generateTLForInstruction _ End = [N,N,N]
-- IO instructions
generateTLForInstruction _ OutputChar = [T,N,S,S]
generateTLForInstruction _ OutputNum = [T,N,S,T]
generateTLForInstruction _ InputChar = [T,N,T,S]
generateTLForInstruction _ InputNum = [T,N,T,T]
-- Other instructions
generateTLForInstruction _ Noop = []
generateTLForInstruction True DebugPrintStack = [N,N,S,S,S]
generateTLForInstruction True DebugPrintHeap = [N,N,S,S,T]
generateTLForInstruction False DebugPrintStack = []
generateTLForInstruction False DebugPrintHeap = []
generateTLForInstruction _ i = error $ "Can not handle instruction " ++ show i

valueToTL :: Integer -> TokenList
valueToTL value = integerToTL value ++ [N]

integerToTL :: Integer -> TokenList
integerToTL value
| 0 <= value = S : naturalToTL (fromIntegral value)
| otherwise = T : naturalToTL (fromIntegral (- value))

naturalToTL :: Natural -> TokenList
naturalToTL value = bitToToken <$> naturalToDigits2 value

stringToTL :: String -> TokenList
stringToTL value = (charToTL =<< value) ++ [N]

charToTL :: Char -> TokenList
charToTL value = bitToToken <$> toBits8 (ord value `mod` 256)

toBits8 :: Int -> [Natural]
toBits8 = toBitsBySize 8

toBitsBySize :: Int -> Int -> [Natural]
toBitsBySize 0 _ = []
toBitsBySize size 0 = [0 | _ <- [1..size]]
toBitsBySize size x
| k == 0 = 0 : toBitsBySize size' x
| otherwise = 1 : toBitsBySize size' (x - k*m)
where
size' = size - 1
m = 2 ^ size'
k = x `div` m
Loading

0 comments on commit 9dc649f

Please sign in to comment.