diff --git a/HLasm.cabal b/Kroha.cabal similarity index 60% rename from HLasm.cabal rename to Kroha.cabal index c915ee8..340df3f 100644 --- a/HLasm.cabal +++ b/Kroha.cabal @@ -4,8 +4,8 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -name: HLasm -version: 0.4.0.0 +name: Kroha +version: 1.0.0.0 description: Please see the README on GitHub at homepage: https://github.com/vorotynsky/HLasm#readme bug-reports: https://github.com/vorotynsky/HLasm/issues @@ -22,18 +22,18 @@ source-repository head type: git location: https://github.com/vorotynsky/HLasm -library - exposed-modules: - HLasm.Ast - HLasm.Backend.Nasm - HLasm.Error - HLasm.Frame - HLasm.Instructions - HLasm.Parser - HLasm.Scope - HLasm.Types +executable Kroha + main-is: Main.hs other-modules: - Paths_HLasm + Kroha + Kroha.Ast + Kroha.Backends.Nasm + Kroha.Instructions + Kroha.Parser + Kroha.Scope + Kroha.Stack + Kroha.Types + Paths_Kroha hs-source-dirs: src build-depends: @@ -42,19 +42,3 @@ library , extra >=1.0 && <1.8 , parsec >=3.1.0 && <=3.1.14.0 default-language: Haskell2010 - -executable HLasm-exe - main-is: Main.hs - other-modules: - Funcs - Paths_HLasm - hs-source-dirs: - app - ghc-options: -threaded -rtsopts -with-rtsopts=-N - build-depends: - HLasm - , base >=4.7 && <5 - , containers >=0.6 && <0.7 - , extra >=1.0 && <1.8 - , parsec >=3.1.0 && <=3.1.14.0 - default-language: Haskell2010 diff --git a/README.md b/README.md index ae59a6b..bebde71 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# HLasm +# Kroha -Improve your assembly experience with HLasm! +Improve your assembly experience with Kroha! This language is more comfortable than a pure assembly. ## Example @@ -11,18 +11,18 @@ Instead of documentation ```asm program { - fake frame + manual frame act { + mov ax, [bp-4] + inc ax + leave + ret + } - frame (main) { + frame main { reg a : ax a = 5 call (a) } - - frame (act) { - !mov ax, [bp-4]; - !inc ax; - } } ``` @@ -30,43 +30,40 @@ Compiled ```asm section .text +act: + mov ax, [bp-4] + inc ax + leave + ret +section .text main: - push bp - mov bp, sp - sub sp, 0 - - mov ax, 5 - push ax - call act - - add sp, 0 + mov ax, 5 + push ax + call act + add sp, 2 leave ret -act: - push bp - mov bp, sp - sub sp, 0 - mov ax, [bp-4] - inc ax -leave -ret ``` ### Variables ```asm program { - var a : int(16) = 32 - var b : int(8) = 1 - const c : int(16) = 32 - const d : int(8) = 1 + var a : int16 = 32 + var b : int8 = 1 + const c : int16 = 32 + const d : int8 = 1 - frame (main) { + manual var arr : &int8 { + times 64 db 0 + } + + frame main { reg ra : ax reg ptr : bx - var sb : int(16) + var sb : int16 ra = 5 sb = 6 ptr = b @@ -77,41 +74,48 @@ program { Compiled ```asm -section .text - -main: - push bp - mov bp, sp - - sub sp, 2 - mov ax, 5 - mov WORD [bp-2], 6 - mov bx, b -leave -ret +section .data +a: dw 32 section .data -a: DW 32 -b: DB 1 +b: db 1 section .rodata -c: DW 32 -d: DB 1 +c: dw 32 + +section .rodata +d: db 1 + +section .data +arr: + times 64 db 0 + + +section .text +main: + mov ax, 5 + mov [bp - 2], 6 + mov bx, [b] +leave +ret ``` ### Conditions and loops ```asm program { - frame (main) { + frame main { reg val : ax val = 0 - while (LOOP) { + loop (LOOP) { if (val > 5, CMP) { break (LOOP) } - !inc ax; + else { + !dec bx + } + !inc ax } } } @@ -121,30 +125,23 @@ Compiled ```asm section .text - main: -push bp - mov bp, sp - sub sp, 0 - mov ax, 0 - LOOPbegin: - cmp ax, 5 - jg CMP1 - jmp CMPend - CMP1: - jmp LOOPend - jmp CMPend - CMPend: - - inc ax - jmp LOOPbegin - LOOPend: + mov ax, 0 + LOOP_begin: + cmp ax, 5 + jg CMP_begin + dec bx + jmp CMP_end + CMP_begin: + jmp LOOP_end + CMP_end: + inc ax + jmp LOOP_begin + LOOP_end: leave ret ``` -> Generated code in these examples was manually formatted. - ## Build and install Build using [stack](https://docs.haskellstack.org). @@ -159,10 +156,10 @@ Install using [stack](https://docs.haskellstack.org). stack install ``` -## Run HLasm +## Run Kroha It compiles each file individually and prints nasm to the terminal. ```sh -HLasm ./file1 ./file2 ./file3 +Kroha ./file1 ./file2 ./file3 ``` diff --git a/app/Funcs.hs b/app/Funcs.hs deleted file mode 100644 index 36a9650..0000000 --- a/app/Funcs.hs +++ /dev/null @@ -1,19 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module Funcs where - -import Control.Monad.Zip - -err :: e -> (a -> Maybe b) -> a -> Either e b -err e f = err e . f - where err _ (Just x) = Right x - err x Nothing = Left x - -get :: Either a a -> a -get (Left x) = x -get (Right x) = x - -join :: String -> [String] -> String -join s [] = "" -join s [x] = x -join s (x:xs) = x ++ s ++ join s xs diff --git a/app/Main.hs b/app/Main.hs deleted file mode 100644 index f1d0fcd..0000000 --- a/app/Main.hs +++ /dev/null @@ -1,32 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module Main where - -import System.Environment (getArgs) -import Control.Monad.Zip -import Data.Bifunctor (first) - -import Funcs -import HLasm.Parser -import HLasm.Scope (semantic) -import HLasm.Types (typeCheck) -import HLasm.Frame (StackFrame(Root), buildStackFrames) -import HLasm.Instructions (program, BackEnd(..), runBackend) -import HLasm.Backend.Nasm - -parseAll :: String -> String -parseAll = get . first show . pipeline - where pipeline src = - do parsed <- parse src - semantic <- semantic parsed - _ <- typeCheck semantic - stack <- Right $ (buildStackFrames Root) parsed - tree <- Right $ mzipWith (\(e, v, l) (_, sf) -> (e, v, l, sf)) semantic stack - objProg <- program tree - runBackend nasm objProg - -main :: IO () -main = do - args <- getArgs - contents <- sequence . fmap readFile $ args - putStrLn . join "\n\n" . fmap (parseAll) $ contents diff --git a/package.yaml b/package.yaml index be1e747..765b207 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ -name: HLasm -version: 0.4.0.0 +name: Kroha +version: 1.0.0.0 github: "vorotynsky/HLasm" license: GPL-3 author: "Vorotynsky Maxim" @@ -21,19 +21,10 @@ description: Please see the README on GitHub at = 4.7 && < 5 - containers >= 0.6 && < 0.7 -- extra >= 1.0 && < 1.8 - parsec >= 3.1.0 && <= 3.1.14.0 - -library: - source-dirs: src +- extra >= 1.0 && < 1.8 executables: - HLasm-exe: - main: Main.hs - source-dirs: app - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N - dependencies: - - HLasm + Kroha: + source-dirs: src + main: Main.hs diff --git a/src/HLasm/Ast.hs b/src/HLasm/Ast.hs deleted file mode 100644 index 86e819d..0000000 --- a/src/HLasm/Ast.hs +++ /dev/null @@ -1,107 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Ast where - -import Data.Tree -import Data.Maybe - -data Type = Type - { typeName :: String - , typeSize :: Maybe Int } - deriving (Show, Eq) - -type VariableName = String -type RegisterName = String -type Label = String - -data CompareType = - Equals - | NotEquals - | Greater - | Less - deriving (Show, Eq) - -data LValue = - NameValue VariableName - | RegisterValue RegisterName - deriving (Eq) - -data RValue = - LeftValue LValue - | IntegerValue Int - deriving (Eq) - -instance Show LValue where - show (NameValue name) = name - show (RegisterValue name) = '%':name - -instance Show RValue where - show (LeftValue value) = show value - show (IntegerValue num) = show num - -data Condition = Condition (RValue, CompareType, RValue) - deriving (Show, Eq) - -data HLElement = - Program - | InstructionSet - | RegisterDeclaration VariableName RegisterName - | VariableDeclaration VariableName Type - | GlobalVarDeclaration VariableName Type RValue - | ConstVarDeclaration VariableName Type RValue - | FakeVariable VariableName - | FakeFrame Label - | Frame (Maybe Label) - | If Label - | IfBranch (Maybe Condition) - | While Label - | DoWhile Label - | Break Label - | Call Label [RValue] - | Assignment LValue RValue - | AssemblyCall String - deriving (Show, Eq) - - -getValuableName :: HLElement -> Maybe VariableName -getValuableName (VariableDeclaration name _ ) = Just name -getValuableName (RegisterDeclaration name _ ) = Just name -getValuableName (GlobalVarDeclaration name _ _) = Just name -getValuableName (ConstVarDeclaration name _ _) = Just name -getValuableName _ = Nothing - -isVariable :: HLElement -> Bool -isVariable = isJust . getValuableName - -variableName :: HLElement -> VariableName -variableName = fromJust . getValuableName - -variableNameValue :: RValue -> Maybe VariableName -variableNameValue (LeftValue (NameValue name)) = Just name -variableNameValue _ = Nothing - -usedVariables :: HLElement -> [VariableName] -usedVariables (IfBranch (Just (Condition(left, _, right)))) = name left ++ name right - where name (LeftValue (NameValue name)) = [name] - name _ = [] -usedVariables (Call _ xs) = fromMaybe [] $ traverse variableNameValue xs -usedVariables (Assignment left (LeftValue right)) = concatMap (maybeToList . variableNameValue . LeftValue) [left, right] -usedVariables (Assignment left _) = fromMaybe [] $ traverse (variableNameValue . LeftValue) [left] -usedVariables _ = [] - -usedLabels :: HLElement -> [Label] -usedLabels (Break label) = [label] -usedLabels (Call label _) = [label] -usedLabels _ = [] - -type SyntaxTree = Tree HLElement - -ftree :: Tree (Tree a) -> Tree a -ftree (Node t []) = t -ftree (Node (Node t f) x) = Node t (f ++ fmap ftree x) - -nestedTree :: Monoid a => [Tree a] -> Tree a -nestedTree xs = ftree $ unfoldTree internal xs - where internal [] = (Node mempty [], []) - internal [x] = (x, []) - internal (x:xs) = (x, [xs]) diff --git a/src/HLasm/Backend/Nasm.hs b/src/HLasm/Backend/Nasm.hs deleted file mode 100644 index 1bb7976..0000000 --- a/src/HLasm/Backend/Nasm.hs +++ /dev/null @@ -1,86 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Backend.Nasm (nasm) where - -import Data.Char - -import HLasm.Ast (CompareType (..), Type(..)) -import HLasm.Error -import HLasm.Frame -import HLasm.Instructions hiding (program) - -bytes :: Int -> Int -bytes x = ceiling ((toEnum x) / 8) - -data DataType = Byte | Word | Dword - deriving (Show, Eq) - -uname = fmap toUpper . show - -dname Byte = "DB" -dname Word = "DW" -dname Dword = "DD" - -datatype :: Int -> DataType -datatype = f . bytes - where f 1 = Byte - f 2 = Word - f 4 = Dword - f n = error ("undefined data size: " ++ show n) - -toDatatype :: Type -> DataType -toDatatype = let size (Type _ (Just s)) = s in datatype . size - -target :: Target -> String -target (NamedTarget name) = name -target (Register reg) = reg -target (FrameVar (offset, size, _)) = (uname . datatype) size ++ " [bp-" ++ (show $ bytes (offset + size)) ++ "]" -target (ConstantTarget const) = show const - -instr2arg :: String -> Target -> Target -> String -instr2arg n l r = n ++ " " ++ target l ++ ", " ++ target r - -size :: StackFrame -> Int -size (Root) = 0 -size (Fluent _) = 0 -size (StackFrame _ x) = bytes $ frameSize x - -frame f = ["push bp", "mov bp, sp", "sub sp, " ++ (show . size $ f)] - -instruction :: Instructions -> [String] -instruction (PureAsm str) = [str] -instruction (BeginFrame f (Just l)) = (l ++ ":"):(frame f) -instruction (EndFrame f (Just _)) = ["leave", "ret"] -instruction (BeginFrame f Nothing) = frame f -instruction (EndFrame f Nothing) = ["leave"] -instruction (Label l) = [l ++ ":"] -instruction (Move l r) = [instr2arg "mov" l r] -instruction (Compare l r) = [instr2arg "cmp" l r] -instruction (Jump lbl Nothing) = ["jmp " ++ lbl] -instruction (Jump lbl (Just Equals)) = ["je " ++ lbl] -instruction (Jump lbl (Just NotEquals)) = ["jne " ++ lbl] -instruction (Jump lbl (Just Greater)) = ["jg " ++ lbl] -instruction (Jump lbl (Just Less)) = ["jl " ++ lbl] -instruction (Call lbl args size) = (fmap push . reverse $ args) ++ ["call " ++ lbl, "add sp, " ++ show (bytes size)] - where push x = "push " ++ (target x) - -variable :: Variable -> String -variable (Variable n t v)= n ++ ": " ++ (dname . toDatatype) t ++ " " ++ show v - -join :: String -> [String] -> String -join s [] = "" -join s [x] = x -join s (x:xs) = x ++ s ++ join s xs - -sectionHeader header = Right . join "\n" . ((:) ("section ." ++ header)) - -section :: Section -> Result String -section (Text x) = sectionHeader "text\n" . concat $ fmap instruction x -section (Data x) = sectionHeader "data" $ fmap variable x -section (Constants x) = sectionHeader "rodata" $ fmap variable x - -program :: ObjProgram -> Result String -program (ObjProgram sections) = fmap (join "\n\n") . traverse section $ sections - -nasm :: BackEnd -nasm = BackEnd program diff --git a/src/HLasm/Error.hs b/src/HLasm/Error.hs deleted file mode 100644 index 9832180..0000000 --- a/src/HLasm/Error.hs +++ /dev/null @@ -1,26 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Error where - -import HLasm.Ast -import qualified Text.Parsec - -data Error = - StringError String - | ParseError Text.Parsec.ParseError - | VariableNotFound VariableName - | LabelNotFound Label - | IncompatibleTypes (RValue, RValue) - | GlobalVariableInFrame VariableName - deriving (Eq) - -type Result a = Either Error a - -instance Show Error where - show (StringError msg) = "error: " ++ msg ++ ".\n" - show (VariableNotFound name) = "scope error: a variable \'" ++ name ++ "\' not found.\n" - show (LabelNotFound label) = "scope error: a label \'" ++ label ++ "\' not found.\n" - show (IncompatibleTypes (left, right)) = - "type error: incompatible types between \'" ++ show left ++ "\' and \'" ++ show right ++ "\'.\n" - show (ParseError err) = "parser error, " ++ show err - show (GlobalVariableInFrame name) = "error: the global variable \'" ++ name ++ "\' isn't in the global scope.\n" diff --git a/src/HLasm/Frame.hs b/src/HLasm/Frame.hs deleted file mode 100644 index babb0d5..0000000 --- a/src/HLasm/Frame.hs +++ /dev/null @@ -1,73 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Frame -( VarFrame(..) -, stackVarSize -, frameSize -, buildFrame -, buildFrameTree -, StackFrame(..) -, buildStackFrames -, findOffset -) where - -import Data.List -import Data.Tree -import HLasm.Ast -import HLasm.Scope hiding (Scope(Root)) - -size (Type _ (Just s)) = s -{- TODO: add support or refactor in future (on adding errors to compiler) -} -size (Type _ Nothing) = error "Unsupported types without specified size" - -stackVarSize :: HLElement -> Int -stackVarSize (VariableDeclaration _ t ) = size t -stackVarSize (GlobalVarDeclaration _ t _) = size t -stackVarSize (ConstVarDeclaration _ t _) = size t -stackVarSize (RegisterDeclaration _ r ) = 0 - -newtype VarFrame = VarFrame [(HLElement, Int, Int)] - deriving (Show, Eq) - -empty :: VarFrame -empty = VarFrame [] - -frameSize :: VarFrame -> Int -frameSize = foldr (+) 0 . fmap stackVarSize . (\(VarFrame xs) -> fmap (\(x,_,_) -> x) xs) - -buildFrame :: [HLElement] -> VarFrame -buildFrame xs = VarFrame $ zipWith (\v o -> (v, o, stackVarSize v)) xs (fmap (foldl (+) 0) . inits . fmap stackVarSize $ xs) - -frameVars :: SyntaxTree -> [HLElement] -frameVars (Node el@(Frame _) []) = [] -frameVars (Node el@(Frame _) (x:_)) = frameVars x - where frameVars (Node val xs) | isVariable val = [val] ++ (concatMap frameVars xs) - frameVars (Node el@(Frame _) _) = [] - frameVars (Node _ xs) = concatMap frameVars xs - -buildFrameTree :: SyntaxTree -> Tree (HLElement, VarFrame) -buildFrameTree t@(Node el@(HLasm.Ast.Frame _) xs) = Node (el, buildFrame . frameVars $ t) (fmap buildFrameTree xs) -buildFrameTree (Node el xs) = Node (el, empty) (fmap buildFrameTree xs) - -data StackFrame = - Root - | Fluent { parentFrame :: StackFrame } - | StackFrame - { parentFrame :: StackFrame - , variables :: VarFrame } - deriving (Show, Eq) - -buildStackFrames :: StackFrame -> SyntaxTree -> Tree (HLElement, StackFrame) -buildStackFrames parent tree@(Node el@(Frame _) xs) = Node (el, frame) (fmap (buildStackFrames frame) xs) - where frame = StackFrame parent . buildFrame $ frameVars tree -buildStackFrames parent (Node el xs) = Node (el, Fluent parent) (fmap (buildStackFrames parent) xs) - -findOffset :: StackFrame -> VariableName -> (Maybe Int) -findOffset Root _ = Nothing -findOffset (Fluent parent) name = findOffset parent name -findOffset (StackFrame parent vars) name - | (any predicate list) = fmap (\(_, o, _) -> o) . find predicate $ list - | otherwise = fmap (+ frameSize vars) $ findOffset parent name -- -> change last commit - where list = (\(VarFrame xs) -> xs) vars - predicate (el, _, _) | isVariable el && (variableName el) == name = True - predicate _ = False diff --git a/src/HLasm/Instructions.hs b/src/HLasm/Instructions.hs deleted file mode 100644 index 258f112..0000000 --- a/src/HLasm/Instructions.hs +++ /dev/null @@ -1,136 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Instructions -( Offset(..), Target(..), InstructionSet(..) -, Instructions(..), Variable(..) -, Section(..), ObjProgram(..) -, BackEnd(..) -, runBackend -, program -) where - -import Control.Monad.Extra (concatMapM) -import Data.List -import Data.Maybe -import Data.Tree -import HLasm.Ast -import HLasm.Frame -import HLasm.Scope -import HLasm.Error -import HLasm.Types - -type Offset = Int -type Size = Int - -data Target = - NamedTarget VariableName - | Register RegisterName - | FrameVar (Offset, Size, VariableName) - | ConstantTarget Int - deriving (Show, Eq) - -data Instructions = - PureAsm String - | BeginFrame StackFrame (Maybe Label) - | EndFrame StackFrame (Maybe Label) - | Label Label - | Move Target Target - | Call Label [Target] Int - | Compare Target Target - | Jump Label (Maybe CompareType) - deriving (Show, Eq) - -type InstructionSet = [Instructions] - -data Variable = Variable VariableName Type RValue - -data Section = - Text InstructionSet - | Data [Variable] - | Constants [Variable] - -newtype ObjProgram = ObjProgram [Section] - -newtype BackEnd = BackEnd (ObjProgram -> Result String) -runBackend (BackEnd f) x = f x - - -target :: StackFrame -> VariableData -> Target -target _ (VariableData (_, (RegisterDeclaration _ reg))) = HLasm.Instructions.Register reg -target frame (VariableData (name, e)) = case findOffset frame name of - Just x -> FrameVar (x, stackVarSize e, name) - Nothing -> NamedTarget name - -findTarget :: StackFrame -> [VariableData] -> VariableName -> Target -- was a lot of checks, target garanteed be here. -findTarget frame xs name = target frame . fromJust . find (\(VariableData (n, _)) -> n == name) $ xs - -valuableTarget :: (StackFrame, [VariableData]) -> RValue -> Target -valuableTarget _ (IntegerValue v) = ConstantTarget v -valuableTarget (sf, vd) (LeftValue(NameValue name)) = findTarget sf vd name -valuableTarget (sf, vd) (LeftValue(RegisterValue name)) = Register name - -loop :: Label -> Result (InstructionSet) -> Result (InstructionSet) -loop lbl i = let begin = lbl ++ "begin" in fmap (\x -> [Label begin] ++ x ++ [Jump begin Nothing, Label (lbl ++ "end")]) $ i - -isEmptyInstruction :: HLElement -> Bool -isEmptyInstruction (FakeVariable _) = True -isEmptyInstruction (FakeFrame _) = True -isEmptyInstruction (VariableDeclaration _ _) = True -isEmptyInstruction (RegisterDeclaration _ _) = True -isEmptyInstruction _ = False - -rval2target :: RValue -> StackFrame -> [VariableData] -> Target -rval2target (IntegerValue val) _ _ = ConstantTarget val -rval2target (LeftValue (RegisterValue name)) _ _ = Register name -rval2target (LeftValue (NameValue name)) s d = findTarget s d name - -instructions :: Tree (HLElement, [VariableData], [LabelData], StackFrame) -> Result (InstructionSet) -instructions (Node (el, _, _, _) _) | isEmptyInstruction el = Right [] -instructions (Node ((InstructionSet ), _, _, _) xs) = concatMapM instructions xs -instructions (Node ((While lbl ), _, _, _) xs) = loop lbl (concatMapM instructions xs) -instructions (Node ((DoWhile lbl ), _, _, _) xs) = loop lbl (concatMapM instructions xs) -instructions (Node ((Break lbl ), _, _, _) _ ) = Right [Jump (lbl ++ "end") Nothing] -instructions (Node ((AssemblyCall str ), _, _, _) _ ) = Right [PureAsm str] -instructions (Node ((Frame lbl ), _, _, f) xs) = - (\body -> [BeginFrame f lbl] ++ body ++ [EndFrame f lbl]) <$> concatMapM instructions xs - -instructions (Node ((GlobalVarDeclaration n _ _), _, _, _) _ ) = Left (GlobalVariableInFrame n) -instructions (Node ((ConstVarDeclaration n _ _), _, _, _) _ ) = Left (GlobalVariableInFrame n) - -instructions (Node ((Assignment left right), d, _, f) _) - = Right [Move (rval2target (LeftValue left) f d) (rval2target right f d)] - -instructions (Node ((HLasm.Ast.Call lbl ns ), d, _, f) _ ) = - Right [HLasm.Instructions.Call lbl (fmap (\n -> rval2target n f d) ns) size] - where size = foldl (+) 0 . fmap (fromMaybe 0 . fmap getSize . literalType d) $ ns - -instructions (Node ((If lbl), _, _, _) []) = Right [] -instructions (Node ((If lbl), _, _, _) xs) = - do (conds, bodies') <- Right $ traverse (uncurry branch) (zip [1..] xs) - bodies <- fmap (concat) . sequence $ bodies' - Right $ conds ++ [Jump (lbl ++ "end") Nothing] ++ bodies ++ [Label (lbl ++ "end")] - where condition lbl pt (Condition (left, cmp, right)) = - let find = valuableTarget pt in [Compare (find left) (find right), Jump lbl (Just cmp)] - wrapif i = fmap (\b -> [Label (lbl ++ show i)] ++ b ++ [Jump (lbl ++ "end") Nothing]) . concatMapM instructions - branch i (Node ((IfBranch (Just cond)), d, _, f) xs) = (condition (lbl ++ show i) (f, d) cond, wrapif i xs) - branch i (Node ((IfBranch Nothing), _, _, _) xs) = ([Jump (lbl ++ show i) Nothing], wrapif i xs) - - -dataFilter (Node ((GlobalVarDeclaration n t v), _, _, _) _) = Just $ Variable n t v -dataFilter _ = Nothing -constFilter (Node ((ConstVarDeclaration n t v), _, _, _) _) = Just $ Variable n t v -constFilter _ = Nothing - -varSection ctor f xs = Right . ctor . fmap fromJust . filter isJust . fmap f $ xs - -filterF :: [(a -> Maybe b)] -> (a -> Bool) -filterF fs = foldl or (const True) . fmap ((.) (not . isJust)) $ fs - where or f g x = f x && g x - -program :: Tree (HLElement, [VariableData], [LabelData], StackFrame) -> Result ObjProgram -program (Node ((Program), _, _, _) xs) = - do text <- concatMapM instructions . filter (filterF [dataFilter, constFilter]) $ xs - dat <- varSection Data dataFilter xs - const <- varSection Constants constFilter xs - Right $ ObjProgram [(Text text), dat, const] -program _ = undefined diff --git a/src/HLasm/Parser.hs b/src/HLasm/Parser.hs deleted file mode 100644 index e618bad..0000000 --- a/src/HLasm/Parser.hs +++ /dev/null @@ -1,105 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -{-# LANGUAGE FlexibleContexts #-} - -module HLasm.Parser (HLasm.Parser.parse) where - -import HLasm.Ast -import HLasm.Error - -import Data.Bifunctor (first) -import Data.Maybe (maybeToList) -import Data.Tree (Tree (..), unfoldTreeM) -import Data.Tuple.Extra (curry3) -import Text.Parsec -import Text.Parsec.String (Parser (..)) - - -treeParser :: (b -> Parser (a, [b])) -> Parser b -> Parser (Tree a) -treeParser f p = p >>= unfoldTreeM f - -leafP f = treeParser (\x -> pure (f x, [])) - -nat :: Parser Int -nat = fmap read (many1 digit) - -aparse :: Parser a -> Parser a -aparse = let spaces = many space in between spaces spaces - -achar = aparse . char -keyword word = aparse $ try (string word <* space) - -around :: Char -> Char -> Parser a -> Parser a -around l r = between (achar l) (achar r) - -parens = around '(' ')' -angles = around '<' '>' -braces = around '{' '}' - -name :: Parser String -name = (:) <$> letter <*> many (alphaNum <|> char '_') - -lvalue' = ((RegisterValue <$> (char '%' *> name)) <|> (NameValue <$> name)) -lvalue = aparse lvalue' -rvalue' = (IntegerValue <$> nat) <|> (LeftValue <$> lvalue') -rvalue = aparse rvalue' - -break = leafP Break (keyword "break" *> parens name) -asmCall = leafP AssemblyCall (spaces *> char '!' *> many1 (noneOf ";") <* char ';' <* spaces) -call = leafP id (Call <$> (keyword "call" *> angles name) <*> parens (rvalue `sepBy` achar ',')) - -vtype = Type <$> name <*> (Just <$> parens nat) -register = leafP id . aparse $ RegisterDeclaration <$> (keyword "reg" *> name) <*> (achar ':' *> name ) -variable = leafP id . aparse $ VariableDeclaration <$> (keyword "var" *> name) <*> (achar ':' *> vtype) - -assignment = leafP id (Assignment <$> lvalue <*> (achar '=' *> rvalue)) -condition = curry3 Condition <$> rvalue <*> cond <*> rvalue - where p x s = const x <$> string s - cond = p Equals "==" <|> p NotEquals "!=" <|> p Greater ">" <|> p Less "<" - -instrSet p = ftree <$> treeParser internal (aparse . many $ aparse p) - where internal x = pure (Node InstructionSet x, []) -block = braces . instrSet - -frame p = (\a b -> Node (Frame a) [b]) <$> fname <*> block p - where fname = (keyword "frame" *> optionMaybe (parens name)) - -ifstatment p = - do (label, ifb) <- ifblock - elseifs <- many $ elseif - elseblk <- optionMaybe $ elseblk - return $ Node (If label) (ifb : elseifs ++ maybeToList elseblk) - where returnBlock cond = block p >>= return . Node (IfBranch cond) . pure - ifblock = do keyword "if" - (condition, label) <- parens ((,) <$> condition <*> (achar ',' *> name)) - block <- returnBlock $ Just condition - return $ (label, block) - elseif = do try (keyword "else" *> keyword "if") - condition <- parens condition - returnBlock $ Just condition - elseblk = keyword "else" >>= const (returnBlock Nothing) - -whileHead = keyword "while" *> parens name -while p = (\l b -> Node (While l) [b]) <$> whileHead <*> (block p) -dowhile p = (\b l -> Node (DoWhile l) [b]) <$> (spaces *> keyword "do" *> block p) <*> whileHead - -hlasm = reduce [ asmCall, call, HLasm.Parser.break, - register, variable, frame hlasm, - ifstatment hlasm, while hlasm, dowhile hlasm, - assignment ] - where reduce (x:xs) = foldl (<|>) x xs - -globalVariable word f = leafP id . aparse $ f <$> (keyword word *> name) <*> (achar ':' *> vtype) <*> (achar '=' *> rvalue) -constant = globalVariable "const" ConstVarDeclaration -globvar = globalVariable "var" GlobalVarDeclaration - -declare = keyword "fake" *> (var <|> label) - where var = leafP FakeVariable (keyword "var" *> angles name <* spaces) - label = leafP FakeFrame (keyword "frame" *> angles name <* spaces) - - -globals = frame hlasm <|> asmCall <|> constant <|> globvar <|> declare -program = (\a -> Node Program a) <$> (keyword "program" *> braces (many globals)) - -parse :: String -> Result SyntaxTree -parse = first ParseError . Text.Parsec.parse program "" diff --git a/src/HLasm/Scope.hs b/src/HLasm/Scope.hs deleted file mode 100644 index 664f592..0000000 --- a/src/HLasm/Scope.hs +++ /dev/null @@ -1,101 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Scope -( semantic -, SemanticTree(..) -, ScopeData(..) -, VariableData(..) -, LabelData(..) -, findVar -, findLabel -, Scope(..) -, elementToScope) where - -import Control.Monad.Zip -import Data.Tree -import Data.Bifunctor -import HLasm.Ast -import HLasm.Error - -data ScopeData = FluentScope - | IntroduceVariable VariableName - | IntroduceLabel Label - deriving (Show, Eq) - -type ScopeTree = Tree ScopeData - -elementToScope :: HLElement -> ScopeData -elementToScope (Program) = FluentScope -elementToScope (InstructionSet) = FluentScope -elementToScope (VariableDeclaration var _) = IntroduceVariable var -elementToScope (RegisterDeclaration var _) = IntroduceVariable var -elementToScope (GlobalVarDeclaration var _ _) = IntroduceVariable var -elementToScope (ConstVarDeclaration var _ _) = IntroduceVariable var -elementToScope (FakeVariable name) = IntroduceVariable name -elementToScope (FakeFrame name) = IntroduceLabel name -elementToScope (Frame (Just label)) = IntroduceLabel label -elementToScope (Frame Nothing) = FluentScope -elementToScope (If label) = IntroduceLabel label -elementToScope (IfBranch _) = FluentScope -elementToScope (While label) = IntroduceLabel label -elementToScope (DoWhile label) = IntroduceLabel label -elementToScope (Break _) = FluentScope -elementToScope (Call _ _) = FluentScope -elementToScope (Assignment _ _) = FluentScope -elementToScope (AssemblyCall _) = FluentScope - -data Scope = - Root - | Scope - { scopeData :: ScopeData - , scopeParent :: Scope - , scopeElement :: HLElement } - deriving (Show, Eq) - -newtype ScopedElement = ScopedElement (HLElement, ScopeData) - -instance Semigroup (ScopedElement) where - a <> b = undefined -instance Monoid (ScopedElement) where - mempty = ScopedElement (InstructionSet, FluentScope) - -foldz :: (c -> a) -> (a -> b -> c) -> a -> [b] -> [c] -foldz _ _ _ [ ] = [] -foldz g f a [x] = [f a x] -foldz g f a (x:xs) = elem : (foldz g f (g elem) xs) - where elem = f a x - -chainScope s (Node (el, sd) xs) = - Node (el, currScope) (foldz (\(Node (_,s) _) -> s) (fromScopeData) s xs) - where currScope = Scope sd s el - -fromScopeData :: Scope -> Tree (HLElement, ScopeData) -> Tree (HLElement, Scope) -fromScopeData s t@(Node (el@Program , sd) xs@(_:_)) = chainScope s t -fromScopeData s t@(Node (el@InstructionSet, sd) xs@(_:_)) = chainScope s t -fromScopeData s (Node (el, sd) xs) = Node (el, currScope) (fmap (fromScopeData currScope) xs) - where currScope = Scope sd s el - -newtype VariableData = VariableData (VariableName, HLElement) deriving (Show) -newtype LabelData = LabelData (Label, HLElement) deriving (Show) - -findVar :: Scope -> VariableName -> Either VariableName VariableData -findVar Root name = Left name -findVar (Scope {scopeData = (IntroduceVariable var), scopeElement = el}) name | var == name = - Right $ VariableData (var, el) -findVar (Scope {scopeParent = p}) name = findVar p name - -findLabel :: Scope -> Label -> Either Label LabelData -findLabel Root label = Left label -findLabel (Scope {scopeData = (IntroduceLabel lbl), scopeElement = el}) label | lbl == label = - Right $ LabelData (lbl, el) -findLabel (Scope {scopeParent = p}) label = findLabel p label - -type SemanticTree = Tree (HLElement, [VariableData], [LabelData]) - -semantic :: SyntaxTree -> Result SemanticTree -semantic t = traverse process . fromScopeData Root $ mzip t (fmap elementToScope t) - where used f u s = sequence . fmap (f s) . u - process (el, scope) = do - vars <- first VariableNotFound $ used findVar usedVariables scope el - labels <- first LabelNotFound $ used findLabel usedLabels scope el - Right (el, vars, labels) diff --git a/src/HLasm/Types.hs b/src/HLasm/Types.hs deleted file mode 100644 index 6e5069d..0000000 --- a/src/HLasm/Types.hs +++ /dev/null @@ -1,69 +0,0 @@ --- Copyright (c) 2020 Vorotynsky Maxim - -module HLasm.Types where - -import Data.Maybe -import Data.Tree -import Data.Bifunctor -import HLasm.Ast -import HLasm.Scope -import HLasm.Error - -typeSuit :: Type -> Type -> Bool -typeSuit (Type "any" _) _ = True -typeSuit _ (Type "any" _) = True -typeSuit (Type lname _) (Type rname _) | (lname /= rname) = False -typeSuit (Type _ l) (Type _ r) = (fromMaybe maxBound l) >= (fromMaybe maxBound r) - -sysregs = ["ip", "sp", "bp", "si", "di", "cs", "ds", "ss", "es", "fs"] - -registerSize :: RegisterName -> Int -registerSize (n:"l") | elem n "abcd" = 8 -registerSize (n:"h") | elem n "abcd" = 8 -registerSize (n:"x") | elem n "abcd" = 16 -registerSize ('e':n:"x") | elem n "abcd" = 32 -registerSize ('r':n:"x") | elem n "abcd" = 64 -registerSize (x) | elem x sysregs = 16 -registerSize ('e':x) | elem x sysregs = 32 -registerSize ('r':x) | elem x sysregs = 64 -registerSize _ = 0 - -registerType = Type "int" . Just . registerSize - -getType :: HLElement -> Type -getType (VariableDeclaration _ t) = t -getType (ConstVarDeclaration _ t _) = t -getType (GlobalVarDeclaration _ t _) = t -getType (RegisterDeclaration _ r) = Type "int" (Just (registerSize r)) -getType (FakeVariable _) = Type "any" Nothing - -getSize :: Type -> Int -getSize (Type "int" (Just size)) = size -getSize _ = 0 - -lookupType :: VariableName -> [VariableData] -> Maybe Type -lookupType name = fmap getType . lookup name . fmap (\(VariableData x) -> x) - -literalType :: [VariableData] -> RValue -> Maybe Type -literalType _ (IntegerValue x) = Just $ Type "int" (Just $ size x) - where size = ceiling . (\x -> log (x + 1) / log 2) . toEnum -literalType s (LeftValue (NameValue name)) = lookupType name s -literalType s (LeftValue (RegisterValue name)) = Just $ registerType name - -err a b = maybe (Left (a, b)) Right - -astCheck :: HLElement -> [VariableData] -> Either (RValue, RValue) () -astCheck (IfBranch (Just (Condition (left, _, right)))) xs = - do leftType <- err left right $ literalType xs left - rightType <- err left right $ literalType xs right - if typeSuit leftType rightType then Right () else err left right Nothing -astCheck (Assignment left right) xs = - let error = err (LeftValue left) right in - do leftType <- error $ literalType xs (LeftValue left) - rightType <- error $ literalType xs right - if typeSuit leftType rightType then Right () else error Nothing -astCheck _ _ = Right () - -typeCheck :: SemanticTree -> Either Error SemanticTree -typeCheck tree = traverse f tree - where f x@(elem, vars, _) = bimap IncompatibleTypes (const x) $ astCheck elem vars diff --git a/src/Kroha.hs b/src/Kroha.hs new file mode 100644 index 0000000..848ff83 --- /dev/null +++ b/src/Kroha.hs @@ -0,0 +1,25 @@ +module Kroha where + +import Control.Monad.Zip (munzip, mzip) +import Data.Bifunctor (first) +import Data.Tree (Tree (..), drawTree) + +import Kroha.Ast (FrameElement (..), selectorProg) +import Kroha.Parser (parse) +import Kroha.Scope (linkProgram, linksTree) +import Kroha.Stack (stack) +import Kroha.Types (resolve, typeCasts) +import Kroha.Instructions(instructions) +import Kroha.Backends.Nasm (runNasm) + + +kroha :: String -> Either String String +kroha src = compile + where compile = do + program <- first id $ parse src + scopes <- first show $ linkProgram program + let programTree = Node (Instructions []) (selectorProg (const $ Instructions []) id program) + types <- first show $ resolve 16 . typeCasts $ mzip (linksTree program) scopes + let stackRanges = stack 16 program + let prepared = instructions stackRanges scopes program + return (runNasm prepared) diff --git a/src/Kroha/Ast.hs b/src/Kroha/Ast.hs new file mode 100644 index 0000000..d9b6f2c --- /dev/null +++ b/src/Kroha/Ast.hs @@ -0,0 +1,99 @@ +-- Copyright (c) 2020 Vorotynsky Maxim + +module Kroha.Ast where + +import Data.Tree +import Data.List (mapAccumR) + +type VariableName = String +type RegisterName = String +type InlinedCode = String +type Label = String + +data TypeName + = TypeName String + | PointerType TypeName + deriving (Show, Eq) + +data Literal + = IntegerLiteral Int + deriving (Show, Eq) + +data LValue + = VariableLVal VariableName + | RegisterLVal RegisterName + deriving (Show, Eq) + +data RValue + = AsRValue LValue + | RLiteral Literal + deriving (Show, Eq) + +data LocalVariable + = StackVariable VariableName TypeName + | RegisterVariable VariableName RegisterName + deriving (Show, Eq) + +data Comparator + = Equals | NotEquals + | Greater | Less + deriving (Show, Eq) + +newtype Condition = Condition (RValue, Comparator, RValue) + deriving (Show, Eq) + +data FrameElement + = Instructions [FrameElement] + | VariableDeclaration LocalVariable + | If Label Condition FrameElement FrameElement + | Loop Label FrameElement + | Break Label + | Call Label [RValue] + | Assignment LValue RValue + | Inline InlinedCode + deriving (Show, Eq) + + +data Declaration + = Frame Label FrameElement + | GlobalVariable VariableName TypeName Literal + | ConstantVariable VariableName TypeName Literal + | ManualFrame Label InlinedCode + | ManualVariable VariableName TypeName InlinedCode + deriving (Show, Eq) + +newtype Program = Program [Declaration] + deriving (Show, Eq) + +type Selector a = FrameElement -> a + +childs :: FrameElement -> [FrameElement] +childs (Instructions xs) = xs +childs (VariableDeclaration x) = [] +childs (If _ _ b e) = [b, e] +childs (Loop _ b) = [b] +childs (Break _) = [] +childs (Call _ _) = [] +childs (Assignment _ _) = [] +childs (Inline _) = [] + +selector :: Selector a -> FrameElement -> Tree a +selector s = unfoldTree (\e -> (s e, childs e)) + +selectorM :: Monad m => Selector (m a) -> FrameElement -> m (Tree a) +selectorM s = unfoldTreeM (\e -> s e >>= (\x -> return (x, childs e))) + + +selectorProg :: (Declaration -> a) -> Selector a -> Program -> Forest a +selectorProg df sf (Program declarations) = fmap mapper declarations + where mapper d@(Frame _ frame) = Node (df d) [selector sf frame] + mapper declaration = Node (df declaration) [] + + +type NodeId = Int + +genId :: Tree a -> Tree NodeId +genId = snd . mapAccumR (\ac b -> (ac + 1, ac)) 0 + +progId :: Program -> Tree NodeId +progId program = genId $ Node () (selectorProg (const ()) (const ()) program) diff --git a/src/Kroha/Backends/Nasm.hs b/src/Kroha/Backends/Nasm.hs new file mode 100644 index 0000000..d97ae86 --- /dev/null +++ b/src/Kroha/Backends/Nasm.hs @@ -0,0 +1,67 @@ +module Kroha.Backends.Nasm where + +import Data.Tree +import Data.List (groupBy, intercalate) +import Control.Monad.Fix (fix) +import Control.Monad (join) +import Data.List.Extra (groupSort) + +import Kroha.Ast +import Kroha.Instructions hiding (target) + +bytes :: Int -> Int +bytes x = ceiling ((toEnum x) / 8) + +label (CommonLabel l) = l +label (BeginLabel l) = l ++ "_begin" +label (EndLabel l) = l ++ "_end" + +target :: Target -> String +target (LiteralTarget (IntegerLiteral num)) = show num +target (StackTarget (offset, _)) = "[bp - " ++ show (bytes offset) ++ "]" +target (RegisterTarget reg) = reg +target (VariableTarget name) = '[' : name ++ "]" + +jump :: Comparator -> String +jump Equals = "je" +jump NotEquals = "jne" +jump Less = "jl" +jump Greater = "jg" + +nasm16I (Body _ i) = [] +nasm16I (Assembly asm) = [asm] +nasm16I (Label lbl) = [label lbl ++ ":"] +nasm16I (Move l r) = ["mov " ++ target l ++ ", " ++ target r] +nasm16I (CallI l args) = (fmap (((++) "push ") . target) . reverse $ args) ++ ["call " ++ label l, "add sp, " ++ show ((length args) * 2)] +nasm16I (Jump l Nothing) = ["jmp " ++ label l] +nasm16I (Jump lbl (Just (l, c, r))) = ["cmp " ++ target l ++ ", " ++ target r, jump c ++ " " ++ label lbl] + +nasmBodyWrap body = body + +makeFix :: Tree [Instruction] -> [String] +makeFix (Node i c) = join . fmap asmFix $ i + where asmFix (Body _ i) = fmap ((++) indent) . bodyWrap $ makeFix (c !! i) + asmFix i = asm i + (asm, indent, bodyWrap) = (nasm16I, " ", id) + +nasmSection :: Section -> [[String]] -> String +nasmSection section declarations = header <> body <> "\n\n" + where header = "section ." ++ section ++ "\n" + body = intercalate "\n" . fmap (intercalate "\n") $ declarations + +nasmType :: TypeName -> String +nasmType (TypeName "int8" ) = "db" +nasmType (TypeName "int16") = "dw" + +nasmDeclaration :: Tree [Instruction] -> Declaration -> [String] +nasmDeclaration t (Frame l _) = [l ++ ":"] ++ makeFix t ++ ["leave", "ret"] +nasmDeclaration _ (GlobalVariable n t (IntegerLiteral l)) = [n ++ ": " ++ nasmType t ++ " " ++ show l] +nasmDeclaration _ (ConstantVariable n t (IntegerLiteral l)) = [n ++ ": " ++ nasmType t ++ " " ++ show l] +nasmDeclaration _ (ManualFrame l c) = [l ++ ":", c] +nasmDeclaration _ (ManualVariable v _ c) = [v ++ ": " ++ c] + + +runNasm :: [(Section, Declaration, Tree [Instruction])] -> String +runNasm = join . map mapper + where mapper (s, d, t) = nasmSection s [nasmDeclaration t d] + diff --git a/src/Kroha/Instructions.hs b/src/Kroha/Instructions.hs new file mode 100644 index 0000000..48cb248 --- /dev/null +++ b/src/Kroha/Instructions.hs @@ -0,0 +1,91 @@ +-- Copyright (c) 2020 Vorotynsky Maxim + +module Kroha.Instructions where + +import Data.Tree +import Data.Maybe (fromJust) +import Data.Foldable (toList) +import Control.Monad.Zip (mzip, mzipWith) + +import Kroha.Ast +import Kroha.Scope +import Kroha.Stack + + +type Section = String + +data Target + = LiteralTarget Literal + | StackTarget StackRange + | RegisterTarget RegisterName + | VariableTarget VariableName + deriving (Show) + +data LabelTarget + = CommonLabel Label + | BeginLabel Label + | EndLabel Label + deriving (Show) + +data Instruction + = Body FrameElement Int + | Assembly String + | Variable VariableName Int + | Label LabelTarget + | Move Target Target + | CallI LabelTarget [Target] + | Jump LabelTarget (Maybe (Target, Comparator, Target)) + deriving (Show) + +type StackOffsetTree = Tree (NodeId, StackRange) + +link2target :: StackOffsetTree -> ScopeLink -> Target +link2target s (ElementLink (VariableDeclaration (StackVariable _ _)) nid) = StackTarget . fromJust . lookup nid $ toList s +link2target _ (ElementLink (VariableDeclaration (RegisterVariable _ reg)) _) = RegisterTarget reg +link2target _ (DeclarationLink declaration _) = let (VariableScope name) = dscope declaration in VariableTarget name + +target :: StackOffsetTree -> Scope -> RValue -> Target +target so s (AsRValue (VariableLVal var)) = link2target so . fromJust . lookup (VariableScope var) $ s +target _ _ (AsRValue (RegisterLVal reg)) = RegisterTarget reg +target _ _ (RLiteral literal) = LiteralTarget literal + +transformCond sot s (Condition (left, cmp, right)) = (target sot s left, cmp, target sot s right) + +instruction :: StackOffsetTree -> Scope -> FrameElement -> [Instruction] +instruction _ _ (Kroha.Ast.Instructions f) = mzipWith Body f [0..] +instruction _ _ (Kroha.Ast.VariableDeclaration _) = [ ] + +instruction sot s (Kroha.Ast.If name cond t f) = [ Jump (BeginLabel name) (Just $ transformCond sot s cond), + Body f 1, + Jump (EndLabel name) Nothing, + Label (BeginLabel name), + Body t 0, + Label (EndLabel name) ] + +instruction _ _ (Kroha.Ast.Loop name body) = [ Label (BeginLabel name), + Body body 0, + Jump (BeginLabel name) Nothing, + Label (EndLabel name) ] + +instruction _ _ (Kroha.Ast.Break loop) = [ Jump (EndLabel loop) Nothing ] +instruction sot s (Kroha.Ast.Call name args) = [ CallI (CommonLabel name) (fmap (target sot s) args) ] +instruction sot s (Kroha.Ast.Assignment l r) = [ Move (target sot s (AsRValue l)) (target sot s r) ] +instruction _ _ (Kroha.Ast.Inline asm) = [ Assembly asm ] + + +declSection :: Declaration -> Section +declSection (Frame _ _) = "text" +declSection (GlobalVariable _ _ _) = "data" +declSection (ConstantVariable _ _ _) = "rodata" +declSection (ManualFrame _ _) = "text" +declSection (ManualVariable _ _ _) = "data" + + +buildDeclaration :: StackOffsetTree -> Tree Scope -> Declaration -> (Section, Declaration, Tree [Instruction]) +buildDeclaration sot (Node _ [scope]) d@(Frame _ frame) = (declSection d, d, instructions) + where instructions = mzipWith (instruction sot) scope (selector id frame) +buildDeclaration _ _ d = (declSection d, d, Node [] []) + +instructions :: Tree StackRange -> Tree Scope -> Program -> [(Section, Declaration, Tree [Instruction])] +instructions offsets (Node _ scopes) p@(Program declarations) = mzipWith (buildDeclaration sot) scopes declarations + where sot = mzip (progId p) offsets diff --git a/src/Kroha/Parser.hs b/src/Kroha/Parser.hs new file mode 100644 index 0000000..c628893 --- /dev/null +++ b/src/Kroha/Parser.hs @@ -0,0 +1,96 @@ +-- Copyright (c) 2020 Vorotynsky Maxim + +{-# LANGUAGE FlexibleContexts #-} + +module Kroha.Parser (Kroha.Parser.parse) where + +import Kroha.Ast + +import Data.Bifunctor (bimap) +import Data.Maybe (maybeToList) +import Data.Tree (Tree (..), unfoldTreeM) +import Data.Tuple.Extra (curry3) +import Text.Parsec +import Text.Parsec.String (Parser (..)) + + +nat :: Parser Int +nat = fmap read (many1 digit) + +aparse :: Parser a -> Parser a +aparse = let spaces = many space in between spaces spaces + +achar = aparse . char +keyword word = aparse $ try (string word <* space) + +around :: Char -> Char -> Parser a -> Parser a +around l r = between (achar l) (achar r) + +parens = around '(' ')' +angles = around '<' '>' +braces = around '{' '}' + +name :: Parser String +name = (:) <$> letter <*> many (alphaNum <|> char '_') + +literal = IntegerLiteral <$> nat +literal' = aparse literal +lvalue' = (VariableLVal <$> name) <|> (RegisterLVal <$> (char '%' *> name)) +lvalue = aparse lvalue' +rvalue' = (RLiteral<$> literal) <|> (AsRValue <$> lvalue') +rvalue = aparse rvalue' + +break = Break <$> (keyword "break" *> parens name) +inline = Inline <$> aparse (char '!' *> many (noneOf "\n")) +call = Call <$> (keyword "call" *> angles name) <*> parens (rvalue `sepBy` achar ',') + +vtype = PointerType <$> (achar '&' *> vtype) + <|> TypeName <$> name + +register = aparse $ VariableDeclaration <$> (RegisterVariable <$> (keyword "reg" *> name) <*> (achar ':' *> name )) +variable = aparse $ VariableDeclaration <$> (StackVariable <$> (keyword "var" *> name) <*> (achar ':' *> vtype)) + +assignment = Assignment <$> lvalue <*> (achar '=' *> rvalue) + +condition = curry3 Condition <$> rvalue <*> cond <*> rvalue + where p x s = const x <$> string s + cond = p Equals "==" <|> p NotEquals "!=" <|> p Greater ">" <|> p Less "<" + +instrSet p = Instructions <$> (aparse . many $ aparse p) +block = braces . instrSet + +loop p = Loop <$> (keyword "loop" *> parens name) <*> (block p) + +ifstatment p = + do keyword "if" + (cond, label) <- parens $ (,) <$> (condition <* achar ',') <*> name + body <- block p + elseb <- (keyword "else" *> block p) <|> (pure $ Instructions []) + return $ If label cond body (elseb) + +hlasm = reduce [ inline, call, Kroha.Parser.break, + register, variable, + ifstatment hlasm, loop hlasm, assignment ] + where reduce (x:xs) = foldl (<|>) x xs + +globalVariable word f = aparse $ f <$> (keyword word *> name) <*> (achar ':' *> vtype) <*> (achar '=' *> literal') +constant = globalVariable "const" ConstantVariable +globvar = globalVariable "var" GlobalVariable + +manual w d = do (try . aparse) (string "manual" *> spaces *> string w) + def <- d + code <- braces (many (satisfy (/= '}'))) + return (def code) + where braces = between (spaces *> char '{') (char '}' <* spaces) + +manualFrame = manual "frame" (ManualFrame <$> (aparse $ name)) +manualVar = manual "var" (ManualVariable <$> (aparse $ name) <*> (achar ':' *> vtype)) + +frame p = Frame <$> fname <*> block p + where fname = (keyword "frame" *> name) + +globals = frame hlasm <|> constant <|> globvar <|> manualFrame <|> manualVar +program = keyword "program" *> braces (many globals) + +parse :: String -> Either String Program +parse = bimap show Program . Text.Parsec.parse program "" diff --git a/src/Kroha/Scope.hs b/src/Kroha/Scope.hs new file mode 100644 index 0000000..a84c3d8 --- /dev/null +++ b/src/Kroha/Scope.hs @@ -0,0 +1,73 @@ +module Kroha.Scope where + +import Control.Monad (join) +import Control.Monad.Zip (mzip, munzip, mzipWith) +import Data.Maybe (mapMaybe) +import Data.Tree (Tree(..)) + +import Kroha.Ast + +data ScopeEffect + = FluentScope + | VariableScope VariableName + | LabelScope Label + deriving (Eq, Show) + +requestVars :: [RValue] -> [ScopeEffect] +requestVars = mapMaybe (fmap VariableScope . rvalueScope) + where rvalueScope (AsRValue (VariableLVal name)) = Just name + rvalueScope _ = Nothing + +scope :: Selector (ScopeEffect, [ScopeEffect]) +scope (Instructions _) = (FluentScope , []) +scope (VariableDeclaration (StackVariable name _)) = (VariableScope name , []) +scope (VariableDeclaration (RegisterVariable name _)) = (VariableScope name , []) +scope (If label (Condition (a, _, b)) _ _) = (LabelScope label , requestVars [a, b]) +scope (Loop label _) = (LabelScope label , []) +scope (Break label) = (FluentScope , LabelScope label:[]) +scope (Call label args) = (FluentScope , LabelScope label : requestVars args) +scope (Assignment lval rval) = (FluentScope , requestVars [AsRValue lval, rval]) +scope (Inline _) = (FluentScope , []) + +dscope :: Declaration -> ScopeEffect +dscope (Frame label _) = LabelScope label +dscope (GlobalVariable name _ _) = VariableScope name +dscope (ConstantVariable name _ _) = VariableScope name +dscope (ManualFrame label _) = LabelScope label +dscope (ManualVariable name _ _) = VariableScope name + +dscope' d = (dscope d, [] :: [ScopeEffect]) + +type Scope = [(ScopeEffect, ScopeLink)] + +toRight _ (Just x) = Right x +toRight x (Nothing) = Left x + +findEither k = toRight k . lookup k + +data ScopeLink + = ElementLink FrameElement NodeId + | DeclarationLink Declaration NodeId + | RootProgramLink NodeId + deriving (Show) + +localScope :: Program -> Tree (ScopeEffect, [ScopeEffect]) +localScope program = Node (FluentScope, []) (selectorProg dscope' scope program) + +linksTree :: Program -> Tree ScopeLink +linksTree program = mzipWith id (Node (RootProgramLink) (selectorProg DeclarationLink ElementLink program)) (progId program) + +scopeTree :: Scope -> Tree (ScopeEffect, ScopeLink) -> Tree Scope +scopeTree parent (Node effect childs) = Node (effect:parent) childScope + where folder acc child = (rootLabel child : fst acc, snd acc ++ [scopeTree (fst acc) child]) + childScope = snd $ foldl folder (effect:parent, []) childs + +linkScope :: Tree ([ScopeEffect], Scope) -> Either (ScopeEffect) (Tree Scope) +linkScope (Node (request, scope) childs) = join . fmap buildTree $ results + where results = traverse (\r -> findEither r scope >>= return . (,) r) request + buildTree request = sequence . traverse (Node request) $ traverse linkScope childs + +linkProgram :: Program -> Either (ScopeEffect) (Tree Scope) +linkProgram program = linkScope (mzip requests scope) + where (changes, requests) = munzip (localScope program) + scope = scopeTree [] (mzip changes (linksTree program)) diff --git a/src/Kroha/Stack.hs b/src/Kroha/Stack.hs new file mode 100644 index 0000000..4750796 --- /dev/null +++ b/src/Kroha/Stack.hs @@ -0,0 +1,25 @@ +module Kroha.Stack where + +import Data.Tree +import Data.List (mapAccumL) + +import Kroha.Ast +import Kroha.Types + +type StackRange = (Int, Int) {-offset, size-} + +stackVar :: PointerSize -> FrameElement -> Int +stackVar ptr (VariableDeclaration (StackVariable _ t)) = typeSize ptr t +stackVar _ _ = 0 + +frame :: PointerSize -> Tree FrameElement -> (Int, Tree StackRange) +frame ptr tree = mapAccumL f 0 tree + where f acc el = let size = stackVar ptr el in (acc + size, (if size > 0 then acc + size else 0, size)) + +stackFrames :: PointerSize -> Program -> [(Int, Tree StackRange)] +stackFrames ptr (Program declarations) = fmap mapper declarations + where mapper (Frame _ f) = frame ptr (selector id f) + mapper _ = (0, Node (0, 0) []) + +stack :: PointerSize -> Program -> Tree StackRange +stack ptr program = Node (0, 0) (fmap (Node (0, 0) . pure . snd) (stackFrames ptr program)) diff --git a/src/Kroha/Types.hs b/src/Kroha/Types.hs new file mode 100644 index 0000000..b4accb9 --- /dev/null +++ b/src/Kroha/Types.hs @@ -0,0 +1,72 @@ +module Kroha.Types where + +import Data.Graph (Tree(..)) +import Data.Bifunctor (bimap) +import Data.Maybe (fromJust) +import Control.Monad (join) + +import Kroha.Ast +import Kroha.Scope + +sysregs = ["ip", "sp", "bp", "si", "di", "cs", "ds", "ss", "es", "fs"] + +registerSize :: RegisterName -> Int +registerSize (n:"l") | elem n "abcd" = 8 +registerSize (n:"h") | elem n "abcd" = 8 +registerSize (n:"x") | elem n "abcd" = 16 +registerSize ('e':n:"x") | elem n "abcd" = 32 +registerSize ('r':n:"x") | elem n "abcd" = 64 +registerSize (x) | elem x sysregs = 16 +registerSize ('e':x) | elem x sysregs = 32 +registerSize ('r':x) | elem x sysregs = 64 +registerSize _ = 0 + +registerType reg = let rs = registerSize reg in if rs > 0 then Just . TypeName $ "int" ++ show rs else Nothing + +type PointerSize = Int + +typeSize :: PointerSize -> TypeName -> Int +typeSize ptr (PointerType _) = ptr +typeSize _ (TypeName ('i':'n':'t':x)) = read x +typeSize _ _ = 0 + +getType :: ScopeLink -> Maybe TypeName +getType (ElementLink (VariableDeclaration (StackVariable _ t)) _) = Just t +getType (ElementLink (VariableDeclaration (RegisterVariable _ r)) _) = registerType r +getType (DeclarationLink (GlobalVariable _ t _) _) = Just t +getType (DeclarationLink (ConstantVariable _ t _) _) = Just t +getType (DeclarationLink (ManualVariable _ t _) _) = Just t +getType _ = Nothing + +rvalType :: Scope -> RValue -> Maybe TypeName +rvalType _ (RLiteral (IntegerLiteral 0 )) = Just . TypeName $ "int1" +rvalType _ (RLiteral (IntegerLiteral x )) = Just . TypeName $ "int" ++ (show $ ceiling (logBase 2 (abs $ toEnum x + 1))) +rvalType s (AsRValue (VariableLVal name)) = join . fmap getType $ lookup (VariableScope name) s +rvalType _ (AsRValue (RegisterLVal reg )) = registerType reg + +type TypeCast = (TypeName, TypeName) + +makeTypeCast :: Scope -> (RValue, RValue) -> TypeCast +makeTypeCast scope values = bimap find find values + where find x = fromJust . rvalType scope $ x -- scope is checked + +casts :: FrameElement -> Scope -> [TypeCast] +casts (Instructions _) _ = [] +casts (VariableDeclaration _) _ = [] +casts (If _ (Condition (a, _, b)) _ _) s = fmap (makeTypeCast s) [(a, b)] +casts (Loop _ _) _ = [] +casts (Break _) _ = [] +casts (Call _ _) _ = [] -- todo: types for call +casts (Assignment lval rval) s = fmap (makeTypeCast s) [(AsRValue lval, rval)] +casts (Inline _) _ = [] + +typeCasts :: Tree (ScopeLink, Scope) -> Tree [TypeCast] +typeCasts = let f ((RootProgramLink _ ), _) = [] + f ((DeclarationLink _ _), _) = [] + f ((ElementLink el _) , scope) = casts el scope + in fmap f + +resolve :: PointerSize -> Tree [TypeCast] -> Either TypeCast (Tree [TypeCast]) +resolve ptr = sequenceA . fmap sequenceA . (fmap . fmap) resolver + where resolver tc = if lsize > 0 && rsize > 0 then Right tc else Left tc + where (lsize, rsize) = let size = typeSize ptr in bimap size size tc diff --git a/src/Main.hs b/src/Main.hs new file mode 100644 index 0000000..2a3fde6 --- /dev/null +++ b/src/Main.hs @@ -0,0 +1,20 @@ +module Main where + +import Kroha +import System.Environment (getArgs) + +get :: Either a a -> a +get (Left a) = a +get (Right a) = a + +join :: String -> [String] -> String +join s [] = "" +join s [x] = x +join s (x:xs) = x ++ s ++ join s xs + +main :: IO () +main = do + args <- getArgs + contents <- sequence . fmap readFile $ args + _ <- putStrLn "-- build with Kroha\n-- see: https://github.com/vorotynsky/Kroha \n" + putStrLn . join "\n\n" . fmap (get . kroha) $ contents