diff --git a/Kroha.cabal b/Kroha.cabal index d3667f7..5fe324b 100644 --- a/Kroha.cabal +++ b/Kroha.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: Kroha -version: 1.2.2.0 +version: 1.3.1.0 description: Please see the README on GitHub at homepage: https://github.com/vorotynsky/Kroha#readme bug-reports: https://github.com/vorotynsky/Kroha/issues @@ -26,25 +26,31 @@ executable Kroha main-is: Main.hs other-modules: Kroha - Kroha.Ast Kroha.Backends.Common Kroha.Backends.Nasm Kroha.Errors Kroha.Instructions - Kroha.Parser + Kroha.Parser.Declarations + Kroha.Parser.Lexer + Kroha.Parser.Statements Kroha.Scope Kroha.Stack + Kroha.Syntax.Declarations + Kroha.Syntax.Primitive + Kroha.Syntax.Statements + Kroha.Syntax.Syntax Kroha.Types Paths_Kroha hs-source-dirs: src + ghc-options: -XTupleSections -XDeriveTraversable -XRankNTypes -XImplicitParams -XTypeFamilies build-depends: base >=4.7 && <5 , comonad >=5 && <5.1 , containers ==0.6.* , extra >=1.0 && <1.8 , hashmap >=1.0.0 && <1.4 - , parsec >=3.1.0 && <=3.1.14.0 + , megaparsec >=8.0.0 && <=10.0.0 default-language: Haskell2010 test-suite Kroha-tests @@ -53,20 +59,26 @@ test-suite Kroha-tests other-modules: Case Kroha - Kroha.Ast Kroha.Backends.Common Kroha.Backends.Nasm Kroha.Errors Kroha.Instructions - Kroha.Parser + Kroha.Parser.Declarations + Kroha.Parser.Lexer + Kroha.Parser.Statements Kroha.Scope Kroha.Stack + Kroha.Syntax.Declarations + Kroha.Syntax.Primitive + Kroha.Syntax.Statements + Kroha.Syntax.Syntax Kroha.Types Main Paths_Kroha hs-source-dirs: test src + ghc-options: -XTupleSections -XDeriveTraversable -XRankNTypes -XImplicitParams -XTypeFamilies build-depends: Diff >=0.2 && <0.5 , HUnit ==1.6.* @@ -75,5 +87,5 @@ test-suite Kroha-tests , containers ==0.6.* , extra >=1.0 && <1.8 , hashmap >=1.0.0 && <1.4 - , parsec >=3.1.0 && <=3.1.14.0 + , megaparsec >=8.0.0 && <=10.0.0 default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 8e37a6f..5f1039f 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: Kroha -version: 1.2.2.0 +version: 1.3.1.0 github: "vorotynsky/Kroha" license: GPL-3 author: "Vorotynsky Maxim" @@ -18,10 +18,17 @@ extra-source-files: # common to point users to the README.md file. description: Please see the README on GitHub at +ghc-options: +- -XTupleSections +- -XDeriveTraversable +- -XRankNTypes +- -XImplicitParams +- -XTypeFamilies + dependencies: - base >= 4.7 && < 5 - containers >= 0.6 && < 0.7 -- parsec >= 3.1.0 && <= 3.1.14.0 +- megaparsec >= 8.0.0 && <= 10.0.0 - extra >= 1.0 && < 1.8 - comonad >= 5 && < 5.1 - hashmap >= 1.0.0 && < 1.4 diff --git a/src/Kroha.hs b/src/Kroha.hs index 4448b45..1cd6f00 100644 --- a/src/Kroha.hs +++ b/src/Kroha.hs @@ -1,18 +1,18 @@ module Kroha (kroha) where -import Data.Bifunctor (first) -import Data.Foldable (toList) -import Data.HashMap (fromList, lookup) +import Data.Bifunctor (first) +import Data.Foldable (toList) +import Data.HashMap (fromList, lookup) -import Kroha.Ast (NodeId, Program, genId, pzip, pzip3) -import Kroha.Backends.Common (runBackend, typeConfig) -import Kroha.Backends.Nasm (nasm) -import Kroha.Errors (Result, showErrors) -import Kroha.Instructions (instructions) -import Kroha.Parser (parse) -import Kroha.Scope (linkProgram) -import Kroha.Stack (stack) -import Kroha.Types (resolve, typeCastsTree) +import Kroha.Backends.Common (runBackend, typeConfig) +import Kroha.Backends.Nasm (nasm) +import Kroha.Errors (Result, showErrors) +import Kroha.Instructions (instructions) +import Kroha.Parser.Declarations (parseProgram) +import Kroha.Scope (linkProgram) +import Kroha.Stack (stack) +import Kroha.Syntax.Declarations (NodeId, Program, genId, pzip, pzip3) +import Kroha.Types (resolve, typeCastsTree) compile :: Program NodeId -> Result String @@ -20,17 +20,15 @@ compile program = do scopes <- linkProgram program let tc = typeConfig nasm casts <- typeCastsTree tc scopes - types <- resolve tc (pzip program casts) + _ <- resolve tc (pzip program casts) let stackRanges = stack tc program let prepared = instructions (pzip3 stackRanges (fmap snd scopes) program) return (runBackend nasm prepared) kroha :: String -> String -> Either String String kroha name src = - case parse name src of + case parseProgram name src of Left err -> Left err Right parsed -> first (showErrors (`Data.HashMap.lookup` rangeTable)) $ compile prog where prog = genId parsed rangeTable = fromList $ toList $ pzip prog parsed - - diff --git a/src/Kroha/Ast.hs b/src/Kroha/Ast.hs deleted file mode 100644 index 1f87c92..0000000 --- a/src/Kroha/Ast.hs +++ /dev/null @@ -1,154 +0,0 @@ --- Copyright (c) 2020 - 2021 Vorotynsky Maxim - -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE RankNTypes #-} - -module Kroha.Ast where - -import Control.Comonad -import Data.Tree -import Data.List (mapAccumR) -import Control.Monad.Zip (mzipWith) - -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 d - = Instructions [FrameElement d] d - | VariableDeclaration LocalVariable d - | If Label Condition (FrameElement d) (FrameElement d) d - | Loop Label (FrameElement d) d - | Break Label d - | Call Label [RValue] d - | Assignment LValue RValue d - | Inline InlinedCode d - deriving (Show, Eq, Functor, Foldable, Traversable) - - -data Declaration d - = Frame Label (FrameElement d) d - | GlobalVariable VariableName TypeName Literal d - | ConstantVariable VariableName TypeName Literal d - | ManualFrame Label InlinedCode d - | ManualVariable VariableName TypeName InlinedCode d - deriving (Show, Eq, Functor, Foldable, Traversable) - -data Program d = Program [Declaration d] d - deriving (Show, Eq, Functor, Foldable, Traversable) - -childs :: FrameElement d -> [FrameElement d] -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 _ _) = [] - -getDeclData :: Declaration d -> d -getDeclData (Frame _ _ d) = d -getDeclData (GlobalVariable _ _ _ d) = d -getDeclData (ConstantVariable _ _ _ d) = d -getDeclData (ManualFrame _ _ d) = d -getDeclData (ManualVariable _ _ _ d) = d - - - -selector :: (FrameElement d -> a) -> FrameElement d -> Tree a -selector s = unfoldTree (\e -> (s e, childs e)) - -selectorProg :: (Declaration d -> a) -> (FrameElement d -> a) -> Program d -> 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 :: Program d -> Program NodeId -genId (Program decls _) = Program (snd $ mapAccumR declId 1 decls) 0 - where genId'' = mapAccumR (\ac b -> (ac + 1, ac)) - declId begin (Frame l fe _) = let (acc, fe') = genId'' (begin + 1) fe in (acc, Frame l fe' begin) - declId begin d = (begin + 1, d $> begin) - -progId :: Program d -> Tree NodeId -progId program = Node 0 $ selectorProg getDeclData extract (genId program) - -instance Comonad FrameElement where - duplicate node@(Instructions c _) = Instructions (map duplicate c) node - duplicate node@(VariableDeclaration v _) = VariableDeclaration v node - duplicate node@(If l c i e _) = If l c (duplicate i) (duplicate e) node - duplicate node@(Loop l b _) = Loop l (duplicate b) node - duplicate node@(Break l _) = Break l node - duplicate node@(Call l a _) = Call l a node - duplicate node@(Assignment l r _) = Assignment l r node - duplicate node@(Inline c _) = Inline c node - - extract (Instructions _ d) = d - extract (VariableDeclaration _ d) = d - extract (If _ _ _ _ d) = d - extract (Loop _ _ d) = d - extract (Break _ d) = d - extract (Call _ _ d) = d - extract (Assignment _ _ d) = d - extract (Inline _ d) = d - -tzip :: FrameElement a -> FrameElement b -> FrameElement (a, b) -tzip (Instructions ca _a) (Instructions cb _b) = Instructions (uncurry tzip <$> zip ca cb) (_a, _b) -tzip (VariableDeclaration va _a) (VariableDeclaration vb _b) | va == vb = VariableDeclaration va (_a, _b) -tzip (If la ca ia ea _a) (If lb cb ib eb _b) | (la, ca) == (lb, cb) = If la ca (tzip ia ib) (tzip ea eb) (_a, _b) -tzip (Loop la ba _a) (Loop lb bb _b) | la == lb = Loop la (tzip ba bb) (_a, _b) -tzip (Break la _a) (Break lb _b) | la == lb = Break la (_a, _b) -tzip (Call la aa _a) (Call lb ab _b) | (la, aa) == (lb, ab) = Call la aa (_a, _b) -tzip (Assignment la ra _a) (Assignment lb rb _b) | (la, ra) == (lb, rb) = Assignment la ra (_a, _b) -tzip (Inline ca _a) (Inline cb _b) | ca == cb = Inline ca (_a, _b) -tzip _ _ = error "can't zip different frame elements" - -dzip :: Declaration a -> Declaration b -> Declaration (a, b) -dzip (Frame la fea _a) (Frame lb feb _b) | la == lb = Frame la (tzip fea feb) (_a, _b) -dzip (GlobalVariable va ta la _a) (GlobalVariable vb tb lb _b) | (va, ta, la) == (vb, tb, lb) = GlobalVariable va ta la (_a, _b) -dzip (ConstantVariable va ta la _a) (ConstantVariable vb tb lb _b) | (va, ta, la) == (vb, tb, lb) = ConstantVariable va ta la (_a, _b) -dzip (ManualFrame la ca _a) (ManualFrame lb cb _b) | (la, ca) == (lb, cb) = ManualFrame la ca (_a, _b) -dzip (ManualVariable va ta ca _a) (ManualVariable vb tb cb _b) | (va, ta, ca) == (vb, tb, cb) = ManualVariable va ta ca (_a, _b) -dzip _ _ = error "can't zip different declarations" - -pzip :: Program a -> Program b -> Program (a, b) -pzip (Program da _a) (Program db _b) = Program (mzipWith dzip da db) (_a, _b) - -pzip3 :: Program a -> Program b -> Program c -> Program (a, b, c) -pzip3 a b c = fmap (\((a, b), c) -> (a, b, c)) (pzip (pzip a b) c) diff --git a/src/Kroha/Backends/Common.hs b/src/Kroha/Backends/Common.hs index bd3b1e9..560cfe0 100644 --- a/src/Kroha/Backends/Common.hs +++ b/src/Kroha/Backends/Common.hs @@ -1,16 +1,16 @@ module Kroha.Backends.Common (Backend(..), runBackend) where -import Kroha.Ast (Declaration(..)) +import Kroha.Syntax.Declarations (Declaration(..)) import Kroha.Types (TypeConfig) import Kroha.Instructions (Instruction(Body), Section) -import Control.Monad (join, void) +import Control.Monad (void) import Data.Tree (Tree(..)) import Data.Char (isSpace) import Data.Semigroup (Min(Min, getMin)) -data Backend = Backend +data Backend = Backend { typeConfig :: TypeConfig , instruction :: Instruction -> [String] , bodyWrap :: [String] -> [String] @@ -20,24 +20,24 @@ data Backend = Backend makeFix :: Backend -> Tree [Instruction] -> [String] -makeFix backend (Node i c) = join . fmap asmFix $ i - where asmFix (Body _ i) = fmap ((++) (indent backend)) . bodyWrap backend $ makeFix backend (c !! i) +makeFix backend (Node i c) = i >>= asmFix + where asmFix (Body _ i) = fmap (indent backend ++) . bodyWrap backend $ makeFix backend (c !! i) asmFix i = instruction backend i unindentManual :: String -> [String] unindentManual code = fmap (drop minIndent) lined where lined = let (h:t) = (\l -> if null l then [""] else l) $ lines code in if null h then t else h:t - filterEmpty = filter (not . null . filter (not . isSpace)) + filterEmpty = filter (not . all isSpace) minIndent = getMin . foldMap (Min . length . takeWhile isSpace) . filterEmpty $ lined backendDeclaration :: Backend -> Declaration () -> Tree [Instruction] -> String -backendDeclaration b decl@(Frame _ frame _) ti = declaration b decl (makeFix b ti) -backendDeclaration b decl@(GlobalVariable _ _ l _) _ = declaration b decl [] -backendDeclaration b decl@(ConstantVariable _ _ l _) _ = declaration b decl [] -backendDeclaration b decl@(ManualFrame _ c _) _ = declaration b decl (unindentManual c) -backendDeclaration b decl@(ManualVariable _ _ c _) _ = declaration b decl (unindentManual c) +backendDeclaration b decl@(Frame {}) ti = declaration b decl (makeFix b ti) +backendDeclaration b decl@(GlobalVariable {}) _ = declaration b decl [] +backendDeclaration b decl@(ConstantVariable {}) _ = declaration b decl [] +backendDeclaration b decl@(ManualFrame _ c _) _ = declaration b decl (unindentManual c) +backendDeclaration b decl@(ManualVariable _ _ c _) _ = declaration b decl (unindentManual c) runBackend :: Backend -> [(Section, Declaration d, Tree [Instruction])] -> String -runBackend backend = join . fmap (mapper) +runBackend backend = (>>= mapper) where mapper (s, d, i) = section backend s (backendDeclaration backend (void d) i) diff --git a/src/Kroha/Backends/Nasm.hs b/src/Kroha/Backends/Nasm.hs index ce3618e..2fa056c 100644 --- a/src/Kroha/Backends/Nasm.hs +++ b/src/Kroha/Backends/Nasm.hs @@ -1,23 +1,20 @@ module Kroha.Backends.Nasm (nasm) where -import Data.Tree import Data.Graph (buildG) -import Data.List (groupBy, intercalate) -import Control.Monad.Fix (fix) -import Control.Monad (join) -import Data.List.Extra (groupSort) +import Data.List (intercalate) import Data.Bifunctor (first) import Data.Maybe (fromJust) -import Kroha.Ast +import Kroha.Syntax.Syntax import Kroha.Backends.Common import Kroha.Types import Kroha.Instructions (Instruction(..), LabelTarget(..), Target(..), Section) import Kroha.Errors bytes :: Int -> Int -bytes x = ceiling ((toEnum x) / 8) +bytes x = ceiling (toEnum x / (8 :: Double)) +label :: LabelTarget -> Label label (CommonLabel l) = l label (BeginLabel l) = l ++ "_begin" label (EndLabel l) = l ++ "_end" @@ -26,6 +23,7 @@ nasmType :: TypeName -> (String, String) nasmType (TypeName "int8" ) = ("db", "byte") nasmType (TypeName "int16") = ("dw", "word") nasmType (PointerType _) = ("dw", "word") +nasmType (TypeName name ) = error $ "[Exception]: Unexpected type `" ++ name ++ "` in backend" size2type :: Int -> TypeName nasmTypeG , nasmTypeL, untyped :: TypeName -> String @@ -45,11 +43,12 @@ jump NotEquals = "jne" jump Less = "jl" jump Greater = "jg" -nasm16I (Body _ i) = [] +nasm16I :: Instruction -> [String] +nasm16I (Body _ _) = [] nasm16I (Assembly asm) = [asm] nasm16I (Label lbl) = [label lbl ++ ":"] nasm16I (Move l r) = ["mov " ++ target nasmTypeL l ++ ", " ++ target untyped r] -nasm16I (CallI l args) = (fmap (((++) "push ") . target nasmTypeL) . reverse $ args) ++ ["call " ++ label l, "add sp, " ++ show ((length args) * 2)] +nasm16I (CallI l args) = (fmap (("push " ++) . target nasmTypeL) . 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 nasmTypeL l ++ ", " ++ target untyped r, jump c ++ " " ++ label lbl] @@ -60,8 +59,8 @@ nasmSection section body = header <> body <> "\n\n" nasmDeclaration :: Declaration d -> [String] -> String nasmDeclaration (Frame l _ _) body = l ++ ":\n" ++ intercalate "\n" body ++ "\nleave\nret" nasmDeclaration (ManualVariable v _ _ _) [body] = v ++ ": " ++ body ++ "\n" -nasmDeclaration (ManualFrame l _ _) body = l ++ ":\n" ++ intercalate "\n" (fmap ((++) " ") body) -nasmDeclaration (ManualVariable v _ _ _) body = v ++ ":\n" ++ intercalate "\n" (fmap ((++) " ") body) +nasmDeclaration (ManualFrame l _ _) body = l ++ ":\n" ++ intercalate "\n" (fmap (" " ++) body) +nasmDeclaration (ManualVariable v _ _ _) body = v ++ ":\n" ++ intercalate "\n" (fmap (" " ++) body) nasmDeclaration (GlobalVariable n t (IntegerLiteral l) _) _ = n ++ ": " ++ nasmTypeG t ++ " " ++ show l nasmDeclaration (ConstantVariable n t (IntegerLiteral l) _) _ = n ++ ": " ++ nasmTypeG t ++ " " ++ show l @@ -69,16 +68,16 @@ litType :: Literal -> Result TypeId litType l@(IntegerLiteral x) | x >= 0 && x < 65536 = Right 2 | otherwise = Left (BackendError (show l ++ " is not in [0; 65536)")) -nasmTypes = TypeConfig +nasmTypes = TypeConfig { types = (fmap . first) TypeName [("int8", 8), ("int16", 16), ("+literal+", 16)] , pointerType = 1 - , registers = zip (join $ fmap (\x -> fmap ((:) x . pure) "lhx") "abcd") (cycle [0, 0, 1]) + , registers = zip ((\x -> fmap ((:) x . pure) "lhx") =<< "abcd") (cycle [0, 0, 1]) , typeCasts = buildG (0, 3) [(0, 2), (1, 2)] , literalType = litType } -size2type size = fromJust . lookup size . fmap (\(a, b) -> (b, a)) $ (types nasmTypes) +size2type size = fromJust . lookup size . fmap (\(a, b) -> (b, a)) $ types nasmTypes -nasm = Backend +nasm = Backend { instruction = nasm16I , bodyWrap = id , indent = " " diff --git a/src/Kroha/Errors.hs b/src/Kroha/Errors.hs index f9c1b02..842a57b 100644 --- a/src/Kroha/Errors.hs +++ b/src/Kroha/Errors.hs @@ -1,13 +1,12 @@ -{-# LANGUAGE TupleSections #-} module Kroha.Errors where -import Kroha.Ast +import Kroha.Syntax.Syntax import Data.Bifunctor (bimap, first) import Data.Either (partitionEithers) import Data.Foldable (toList) import Data.List.Extra -import Data.Maybe (fromMaybe) -import Text.Parsec (SourcePos, sourceName, sourceColumn, sourceLine) +import Text.Megaparsec (SourcePos, sourceName, sourceColumn, sourceLine) +import Text.Megaparsec.Pos (unPos) type Result a = Either Error a @@ -21,6 +20,7 @@ data Error | BackendError String {- -} deriving Eq +getErrorId :: Error -> NodeId getErrorId (JoinedError _) = -1 getErrorId (TypeCastError _ _ i) = i getErrorId (UnknownType _ i) = i @@ -37,7 +37,7 @@ firstE :: (a -> Error) -> Either a b -> Either Error b firstE = first partitionErrors :: [Either a b] -> Either [a] [b] -partitionErrors e = let (a, b) = partitionEithers e in if (null a) then Right b else Left a +partitionErrors e = let (a, b) = partitionEithers e in if null a then Right b else Left a sequenceErrors :: (Foldable f, Functor f) => ([a] -> c) -> f (Either a b) -> Either c (f b) sequenceErrors f e = bimap f (const g) $ partitionErrors (toList e) @@ -49,20 +49,15 @@ toErrorList = concatMap mapper mapper error = [error] -sNub :: Eq a => [a] -> [a] -sNub (a:b:tail) | a == b = sNub (b:tail) - | otherwise = a:sNub (b:tail) -sNub xs = xs - showErrors :: (NodeId -> Maybe (SourcePos, SourcePos)) -> Error -> String showErrors findRange = intercalate "\n" . fmap (uncurry showError) . process . toErrorList . pure - where showError r (JoinedError _) = undefined - showError r (TypeCastError t1 t2 d) = r ++ "[Type error]:\t" ++ "Can't cast from " ++ show t1 ++ " to " ++ show t2 - showError r (UnknownType t d) = r ++ "[Type error]:\t" ++ "Unknown type " ++ show t - showError r (UnknownRegister reg d) = r ++ "[Type error]:\t" ++ "Unknown register name " ++ show reg - showError r (VariableNotFound var d) = r ++ "[Scope error]:\t" ++ "Variable " ++ var ++ " not found in the scope" - showError r (LabelNotFound label d) = r ++ "[Scope error]:\t" ++ "Label " ++ label ++ " not found in the scope" - showError r (BackendError message) = "[Asm error]: \n" ++ (unlines . fmap ((++) "\t") . lines) message - showRange' (begin, end) = sourceName begin ++ ":" ++ show (sourceLine begin) ++ ":" ++ show (sourceColumn begin) ++ ":\t" + where showError _ (JoinedError _) = undefined + showError r (TypeCastError t1 t2 _) = r ++ "[Type error]:\t" ++ "Can't cast from " ++ show t1 ++ " to " ++ show t2 + showError r (UnknownType t _) = r ++ "[Type error]:\t" ++ "Unknown type " ++ show t + showError r (UnknownRegister reg _) = r ++ "[Type error]:\t" ++ "Unknown register name " ++ show reg + showError r (VariableNotFound var _) = r ++ "[Scope error]:\t" ++ "Variable " ++ var ++ " not found in the scope" + showError r (LabelNotFound label _) = r ++ "[Scope error]:\t" ++ "Label " ++ label ++ " not found in the scope" + showError _ (BackendError message) = "[Asm error]: \n" ++ (unlines . fmap ("\t" ++) . lines) message + showRange' (begin, _) = sourceName begin ++ ":" ++ show (unPos $ sourceLine begin) ++ ":" ++ show (unPos $ sourceColumn begin) ++ ":\t" zipFrom (a, b) = fmap (a, ) b process = concatMap (zipFrom . bimap (maybe "" showRange') nub) . groupSort . map (\x -> (findRange $ getErrorId x, x)) diff --git a/src/Kroha/Instructions.hs b/src/Kroha/Instructions.hs index c46997f..f0f328d 100644 --- a/src/Kroha/Instructions.hs +++ b/src/Kroha/Instructions.hs @@ -6,9 +6,9 @@ import Data.Tree import Data.Maybe (fromJust) import Data.Foldable (toList) import Control.Monad (void) -import Control.Monad.Zip (mzip, mzipWith) +import Control.Monad.Zip (mzipWith) -import Kroha.Ast +import Kroha.Syntax.Syntax import Kroha.Scope import Kroha.Stack import Kroha.Types @@ -32,7 +32,6 @@ data LabelTarget data Instruction = Body (FrameElement ()) Int | Assembly String - | Variable VariableName Int | Label LabelTarget | Move Target Target | CallI LabelTarget [Target] @@ -45,6 +44,7 @@ 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 (declType declaration) +link2target _ l = error ("[Exception]: Unexpected link for building target. Problem link: " ++ show l) target :: StackOffsetTree -> Scope -> RValue -> Target target so s (AsRValue (VariableLVal var)) = link2target so . fromJust . lookup (VariableScope var) $ s @@ -54,25 +54,25 @@ target _ _ (RLiteral literal) = LiteralTarget literal transformCond sot s (Condition (left, cmp, right)) = (target sot s left, cmp, target sot s right) instruction :: StackOffsetTree -> FrameElement Scope -> [Instruction] -instruction _ (Kroha.Ast.Instructions f _) = mzipWith Body (fmap void f) [0..] -instruction _ (Kroha.Ast.VariableDeclaration _ _) = [ ] - -instruction sot (Kroha.Ast.If name cond t f s) = [ Jump (BeginLabel name) (Just $ transformCond sot s cond), - Body (void f) 1, - Jump (EndLabel name) Nothing, - Label (BeginLabel name), - Body (void t) 0, - Label (EndLabel name) ] - -instruction _ (Kroha.Ast.Loop name body _) = [ Label (BeginLabel name), - Body (void body) 0, - Jump (BeginLabel name) Nothing, - Label (EndLabel name) ] - -instruction _ (Kroha.Ast.Break loop _) = [ Jump (EndLabel loop) Nothing ] -instruction sot (Kroha.Ast.Call name args s) = [ CallI (CommonLabel name) (fmap (target sot s) args) ] -instruction sot (Kroha.Ast.Assignment l r s) = [ Move (target sot s (AsRValue l)) (target sot s r) ] -instruction _ (Kroha.Ast.Inline asm _) = [ Assembly asm ] +instruction _ (Instructions f _) = mzipWith Body (fmap void f) [0..] +instruction _ (VariableDeclaration _ _) = [ ] + +instruction sot (If name cond t f s) = [ Jump (BeginLabel name) (Just $ transformCond sot s cond), + Body (void f) 1, + Jump (EndLabel name) Nothing, + Label (BeginLabel name), + Body (void t) 0, + Label (EndLabel name) ] + +instruction _ (Loop name body _) = [ Label (BeginLabel name), + Body (void body) 0, + Jump (BeginLabel name) Nothing, + Label (EndLabel name) ] + +instruction _ (Break loop _) = [ Jump (EndLabel loop) Nothing ] +instruction sot (Call name args s) = [ CallI (CommonLabel name) (fmap (target sot s) args) ] +instruction sot (Assignment l r s) = [ Move (target sot s (AsRValue l)) (target sot s r) ] +instruction _ (Inline asm _) = [ Assembly asm ] declSection :: Declaration d -> Section diff --git a/src/Kroha/Parser.hs b/src/Kroha/Parser.hs deleted file mode 100644 index 8c98602..0000000 --- a/src/Kroha/Parser.hs +++ /dev/null @@ -1,105 +0,0 @@ --- Copyright (c) 2020 - 2021 Vorotynsky Maxim - -{-# LANGUAGE FlexibleContexts #-} - -module Kroha.Parser (Kroha.Parser.parse) where - -import Kroha.Ast -import Kroha.Errors - -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 (..)) - - -nat :: Parser Int -nat = fmap read (many1 digit) - -aparse :: Parser a -> Parser a -aparse = let spaces = many space in between spaces spaces - -krP p = do - begin <- getPosition - value <- p - end <- getPosition - return $ value (begin, end) - -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 = krP $ Break <$> (keyword "break" *> parens name) -inline = krP $ Inline <$> aparse (char '!' *> many (noneOf "\n")) -call = krP $ Call <$> (keyword "call" *> angles name) <*> parens (rvalue `sepBy` achar ',') - -vtype = PointerType <$> (achar '&' *> vtype) - <|> TypeName <$> name - -register = krP . aparse $ VariableDeclaration <$> (RegisterVariable <$> (keyword "reg" *> name) <*> (achar ':' *> name )) -variable = krP . aparse $ VariableDeclaration <$> (StackVariable <$> (keyword "var" *> name) <*> (achar ':' *> vtype)) - -assignment = krP $ 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 = krP . braces . instrSet - -loop p = krP $ Loop <$> (keyword "loop" *> parens name) <*> block p - -ifstatment p = krP $ - do keyword "if" - (cond, label) <- parens $ (,) <$> (condition <* achar ',') <*> name - body <- block p - elseb <- (keyword "else" *> block p) <|> krP (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 = krP $ frame hlasm <|> constant <|> globvar <|> manualFrame <|> manualVar -program = krP $ fmap Program parser - where parser = keyword "program" *> braces (many globals) - - -parse :: String -> String -> Either String (Program (SourcePos, SourcePos)) -parse name = first show . Text.Parsec.parse program name diff --git a/src/Kroha/Parser/Declarations.hs b/src/Kroha/Parser/Declarations.hs new file mode 100644 index 0000000..7229c12 --- /dev/null +++ b/src/Kroha/Parser/Declarations.hs @@ -0,0 +1,41 @@ +-- Copyright (c) 2021 Vorotynsky Maxim + +module Kroha.Parser.Declarations where + +import Kroha.Parser.Lexer +import Kroha.Syntax.Declarations (Declaration(..), Program (Program)) +import Text.Megaparsec +import Data.Bifunctor (first) +import Kroha.Parser.Statements (body, statement) +import Control.Monad (void) + +globalVariable w f = f <$> (w *> name) <*> typeSpecification <*> (symbol "=" *> literal) + +constant = globalVariable const' ConstantVariable +variable = globalVariable var' GlobalVariable + +manuals ps = do + manual' + decl <- foldl (<|>) empty ps + code <- braces (many (noneOf "}")) + return (decl code) + +manualDeclarations = manuals + [ ManualFrame <$> (frame' *> name) + , ManualVariable <$> (var' *> name) <*> typeSpecification] + +frame = Frame <$> (frame' *> name) <*> body statement + +globals = recover (choice (fmap krP [constant, variable, manualDeclarations, frame]) "declaration") + where recover = withRecovery $ \e -> do + registerParseError e + krP skip + skip = do someTill (satisfy (const True)) (const' <|> var' <|> manual' <|> frame') + return (ManualFrame "" "'") + +program = krP $ Program <$> prog (many globals) <* endOfFile + where prog p = (program' *> (braces p <|> symbol ";" *> p)) <|> p + endOfFile = eof <|> void (symbol "=======" *> lexeme (some (noneOf "=") <* symbol "=======")) "end of file" + +parseProgram :: String -> String -> Either String (Program (SourcePos, SourcePos)) +parseProgram name = first errorBundlePretty . runParser program name diff --git a/src/Kroha/Parser/Lexer.hs b/src/Kroha/Parser/Lexer.hs new file mode 100644 index 0000000..f73da82 --- /dev/null +++ b/src/Kroha/Parser/Lexer.hs @@ -0,0 +1,68 @@ +-- Copyright (c) 2021 Vorotynsky Maxim + +module Kroha.Parser.Lexer where + +import Control.Monad (void) +import Data.Void (Void) +import Text.Megaparsec +import Text.Megaparsec.Char +import qualified Text.Megaparsec.Char.Lexer as L + +import Kroha.Syntax.Primitive + + +type Parser = Parsec Void String + +sc :: Parser () +sc = L.space space1 empty empty + +lexeme :: Parser a -> Parser a +lexeme = L.lexeme sc + +symbol :: String -> Parser String +symbol = L.symbol sc + +end :: Parser () +end = void . lexeme . optional . char $ ';' + +nat :: Parser Int +nat = lexeme L.decimal + +krP p = do + begin <- getSourcePos + x <- p + end <- getSourcePos + return $ x (begin, end) + +around l r = between (symbol l) (symbol r) + +parens = around "(" ")" +braces = around "{" "}" +parensOpt p = parens p <|> p + + +name, name' :: Parser String +name' = (:) <$> letterChar <*> many (alphaNumChar <|> char '_') +name = lexeme (name' "identifier") + +callName = around "<" ">" name <|> name + +literal = IntegerLiteral <$> nat "integer literal" +lvalue = (VariableLVal <$> name "variable name") <|> (RegisterLVal <$> lexeme (char '%' *> name') "register name") +rvalue = (RLiteral <$> literal) <|> (AsRValue <$> lvalue) + +typeName = (PointerType <$> lexeme (char '&' *> typeName)) <|> (TypeName <$> name) "type name" +typeSpecification = symbol ":" *> typeName "type specification" + +-- keywords -- +break' = symbol "break" +call' = symbol "call" +if' = symbol "if" +else' = symbol "else" +loop' = symbol "loop" +reg' = symbol "reg" +var' = symbol "var" +frame' = symbol "frame" +const' = symbol "const" +manual' = symbol "manual" +program' = symbol "program" diff --git a/src/Kroha/Parser/Statements.hs b/src/Kroha/Parser/Statements.hs new file mode 100644 index 0000000..369adf5 --- /dev/null +++ b/src/Kroha/Parser/Statements.hs @@ -0,0 +1,47 @@ +-- Copyright (c) 2021 Vorotynsky Maxim + +module Kroha.Parser.Statements where + +import Kroha.Syntax.Syntax +import Kroha.Parser.Lexer +import Text.Megaparsec +import Data.Tuple.Extra (curry3) +import Text.Megaparsec.Char (space1) + +break = krP $ Break <$> (break' *> parensOpt name) +inline = krP (Inline <$> (symbol "!" *> many (noneOf "\n"))) <* space1 +call = krP $ Call <$> (call' *> callName) <*> parens (rvalue `sepBy` symbol ",") + +register = krP $ VariableDeclaration <$> (RegisterVariable <$> (reg' *> name) <*> (symbol ":" *> (name "register name"))) +variable = krP $ VariableDeclaration <$> (StackVariable <$> (var' *> name) <*> typeSpecification) + +assignment = krP $ Assignment <$> try (lvalue <* symbol "=") <*> rvalue + +body pStatement = krP $ braces (Instructions <$> many pStatement) +body' pStatement = body pStatement <|> pStatement + +ifStatement pStatement = krP $ + do if' + (condition, label) <- parens $ (,) <$> (pCondition <* symbol ",") <*> name + body <- body' pStatement + elze <- (else' *> body' pStatement) <|> krP (pure (Instructions [])) + return $ If label condition body elze + where pCondition = curry3 Condition <$> rvalue <*> cmpToken <*> rvalue + p x s = x <$ symbol s + cmpToken = p Equals "==" <|> p NotEquals "!=" <|> p Greater ">" <|> p Less "<" + +loop ps = krP $ Loop <$> (loop' *> parensOpt name) <*> body' ps + +statement = recover (choice ( fmap (<* end) + [ inline, call, Kroha.Parser.Statements.break, + register, variable, assignment ] + ++ + [ ifStatement statement, loop statement ] + ) + "statement") + where recover = withRecovery $ \e -> do + registerParseError e + krP (skip ";\n}") + skip s = do some (noneOf s) + oneOf s + return $ Instructions [] diff --git a/src/Kroha/Scope.hs b/src/Kroha/Scope.hs index 078dc1b..3d0608c 100644 --- a/src/Kroha/Scope.hs +++ b/src/Kroha/Scope.hs @@ -1,18 +1,14 @@ -- Copyright (c) 2020 - 2021 Vorotynsky Maxim -{-# LANGUAGE TupleSections #-} - module Kroha.Scope where import Control.Comonad (extract) -import Control.Monad (void) -import Control.Monad.Zip (munzip, mzip, mzipWith) -import Data.Bifunctor (first) +import Control.Monad.Zip (munzip, mzip) import Data.Foldable (find) import Data.Maybe (fromJust, mapMaybe) import Data.Tree (Tree (..)) -import Kroha.Ast +import Kroha.Syntax.Syntax import Kroha.Errors @@ -52,7 +48,7 @@ dscope' d = ([dscope d], [] :: [RequestFromScope]) type Scope = [(PushToScope, ScopeLink)] toRight _ (Just x) = Right x -toRight x (Nothing) = Left x +toRight x Nothing = Left x findEither k = toRight k . lookup k @@ -69,9 +65,9 @@ linksTree :: Program NodeId -> Tree ScopeLink linksTree program = Node (RootProgramLink program) $ selectorProg DeclarationLink ElementLink program scopeTree :: Scope -> Tree ([PushToScope], ScopeLink) -> Tree Scope -scopeTree parent (Node effect childs) = Node (eZip effect ++ parent) childScope +scopeTree parent (Node effect children) = Node (eZip effect ++ parent) childScope where folder acc child = ((eZip . rootLabel) child ++ fst acc, snd acc ++ [scopeTree (fst acc) child]) - childScope = snd $ foldl folder (eZip effect ++ parent, []) childs + childScope = snd $ foldl folder (eZip effect ++ parent, []) children eZip (p, l) = fmap (, l) p linkScope :: Tree (NodeId, ([ScopeEffect], Scope)) -> Result (Tree Scope) diff --git a/src/Kroha/Stack.hs b/src/Kroha/Stack.hs index f515b93..2034366 100644 --- a/src/Kroha/Stack.hs +++ b/src/Kroha/Stack.hs @@ -4,7 +4,7 @@ import Control.Comonad (extract, duplicate, ($>)) import Data.List (mapAccumL) import Data.Maybe (fromJust) -import Kroha.Ast +import Kroha.Syntax.Syntax import Kroha.Types type StackRange = (Int, Int) {-offset, size-} @@ -19,7 +19,7 @@ frame ptr = snd . mapAccumL f 0 . duplicate where f acc el = let size = stackVar ptr el in (acc + size, (acc, (if size > 0 then acc + size else 0, size))) stackFrames :: TypeConfig -> Program d -> Program (Int, StackRange) -stackFrames ptr p@(Program declarations _) = Program (fmap mapper declarations) (0, (0, 0)) +stackFrames ptr (Program declarations _) = Program (fmap mapper declarations) (0, (0, 0)) where mapper (Frame l f _) = let f' = frame ptr f in Frame l f' (extract f') mapper d = d $> (0, (0, 0)) diff --git a/src/Kroha/Syntax/Declarations.hs b/src/Kroha/Syntax/Declarations.hs new file mode 100644 index 0000000..7c296b6 --- /dev/null +++ b/src/Kroha/Syntax/Declarations.hs @@ -0,0 +1,60 @@ +-- Copyright (c) 2021 Vorotynsky Maxim + +module Kroha.Syntax.Declarations where + +import Data.Graph (Forest, Tree(..)) +import Data.List (mapAccumR) +import Control.Comonad (($>), Comonad (extract)) +import Control.Monad.Zip (mzipWith) + +import Kroha.Syntax.Primitive +import Kroha.Syntax.Statements + +data Declaration d + = Frame Label (FrameElement d) d + | GlobalVariable VariableName TypeName Literal d + | ConstantVariable VariableName TypeName Literal d + | ManualFrame Label InlinedCode d + | ManualVariable VariableName TypeName InlinedCode d + deriving (Show, Eq, Functor, Foldable, Traversable) + +data Program d = Program [Declaration d] d + deriving (Show, Eq, Functor, Foldable, Traversable) + +getDeclData :: Declaration d -> d +getDeclData (Frame _ _ d) = d +getDeclData (GlobalVariable _ _ _ d) = d +getDeclData (ConstantVariable _ _ _ d) = d +getDeclData (ManualFrame _ _ d) = d +getDeclData (ManualVariable _ _ _ d) = d + +selectorProg :: (Declaration d -> a) -> (FrameElement d -> a) -> Program d -> 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 :: Program d -> Program NodeId +genId (Program decls _) = Program (snd $ mapAccumR declId 1 decls) 0 + where genId'' = mapAccumR (\ac b -> (ac + 1, ac)) + declId begin (Frame l fe _) = let (acc, fe') = genId'' (begin + 1) fe in (acc, Frame l fe' begin) + declId begin d = (begin + 1, d $> begin) + +progId :: Program d -> Tree NodeId +progId program = Node 0 $ selectorProg getDeclData extract (genId program) + +dzip :: Declaration a -> Declaration b -> Declaration (a, b) +dzip (Frame la fea _a) (Frame lb feb _b) | la == lb = Frame la (tzip fea feb) (_a, _b) +dzip (GlobalVariable va ta la _a) (GlobalVariable vb tb lb _b) | (va, ta, la) == (vb, tb, lb) = GlobalVariable va ta la (_a, _b) +dzip (ConstantVariable va ta la _a) (ConstantVariable vb tb lb _b) | (va, ta, la) == (vb, tb, lb) = ConstantVariable va ta la (_a, _b) +dzip (ManualFrame la ca _a) (ManualFrame lb cb _b) | (la, ca) == (lb, cb) = ManualFrame la ca (_a, _b) +dzip (ManualVariable va ta ca _a) (ManualVariable vb tb cb _b) | (va, ta, ca) == (vb, tb, cb) = ManualVariable va ta ca (_a, _b) +dzip _ _ = error "can't zip different declarations" + +pzip :: Program a -> Program b -> Program (a, b) +pzip (Program da _a) (Program db _b) = Program (mzipWith dzip da db) (_a, _b) + +pzip3 :: Program a -> Program b -> Program c -> Program (a, b, c) +pzip3 a b c = fmap (\((a, b), c) -> (a, b, c)) (pzip (pzip a b) c) + diff --git a/src/Kroha/Syntax/Primitive.hs b/src/Kroha/Syntax/Primitive.hs new file mode 100644 index 0000000..4555a8e --- /dev/null +++ b/src/Kroha/Syntax/Primitive.hs @@ -0,0 +1,39 @@ +module Kroha.Syntax.Primitive where + +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) + diff --git a/src/Kroha/Syntax/Statements.hs b/src/Kroha/Syntax/Statements.hs new file mode 100644 index 0000000..0c4a571 --- /dev/null +++ b/src/Kroha/Syntax/Statements.hs @@ -0,0 +1,62 @@ +-- Copyright (c) 2021 Vorotynsky Maxim + +module Kroha.Syntax.Statements where + +import Data.Tree (Tree, unfoldTree) +import Control.Comonad + +import Kroha.Syntax.Primitive + +data FrameElement d + = Instructions [FrameElement d] d + | VariableDeclaration LocalVariable d + | If Label Condition (FrameElement d) (FrameElement d) d + | Loop Label (FrameElement d) d + | Break Label d + | Call Label [RValue] d + | Assignment LValue RValue d + | Inline InlinedCode d + deriving (Show, Eq, Functor, Foldable, Traversable) + +children :: FrameElement d -> [FrameElement d] +children (Instructions xs _) = xs +children (VariableDeclaration x _) = [] +children (If _ _ b e _) = [b, e] +children (Loop _ b _) = [b] +children (Break _ _) = [] +children (Call _ _ _) = [] +children (Assignment _ _ _) = [] +children (Inline _ _) = [] + +selector :: (FrameElement d -> a) -> FrameElement d -> Tree a +selector s = unfoldTree (\e -> (s e, children e)) + +instance Comonad FrameElement where + duplicate node@(Instructions c _) = Instructions (map duplicate c) node + duplicate node@(VariableDeclaration v _) = VariableDeclaration v node + duplicate node@(If l c i e _) = If l c (duplicate i) (duplicate e) node + duplicate node@(Loop l b _) = Loop l (duplicate b) node + duplicate node@(Break l _) = Break l node + duplicate node@(Call l a _) = Call l a node + duplicate node@(Assignment l r _) = Assignment l r node + duplicate node@(Inline c _) = Inline c node + + extract (Instructions _ d) = d + extract (VariableDeclaration _ d) = d + extract (If _ _ _ _ d) = d + extract (Loop _ _ d) = d + extract (Break _ d) = d + extract (Call _ _ d) = d + extract (Assignment _ _ d) = d + extract (Inline _ d) = d + +tzip :: FrameElement a -> FrameElement b -> FrameElement (a, b) +tzip (Instructions ca _a) (Instructions cb _b) = Instructions (uncurry tzip <$> zip ca cb) (_a, _b) +tzip (VariableDeclaration va _a) (VariableDeclaration vb _b) | va == vb = VariableDeclaration va (_a, _b) +tzip (If la ca ia ea _a) (If lb cb ib eb _b) | (la, ca) == (lb, cb) = If la ca (tzip ia ib) (tzip ea eb) (_a, _b) +tzip (Loop la ba _a) (Loop lb bb _b) | la == lb = Loop la (tzip ba bb) (_a, _b) +tzip (Break la _a) (Break lb _b) | la == lb = Break la (_a, _b) +tzip (Call la aa _a) (Call lb ab _b) | (la, aa) == (lb, ab) = Call la aa (_a, _b) +tzip (Assignment la ra _a) (Assignment lb rb _b) | (la, ra) == (lb, rb) = Assignment la ra (_a, _b) +tzip (Inline ca _a) (Inline cb _b) | ca == cb = Inline ca (_a, _b) +tzip _ _ = error "can't zip different frame elements" diff --git a/src/Kroha/Syntax/Syntax.hs b/src/Kroha/Syntax/Syntax.hs new file mode 100644 index 0000000..0db6358 --- /dev/null +++ b/src/Kroha/Syntax/Syntax.hs @@ -0,0 +1,9 @@ +module Kroha.Syntax.Syntax +( module Kroha.Syntax.Primitive +, module Kroha.Syntax.Statements +, module Kroha.Syntax.Declarations +) where + +import Kroha.Syntax.Primitive +import Kroha.Syntax.Statements +import Kroha.Syntax.Declarations diff --git a/src/Kroha/Types.hs b/src/Kroha/Types.hs index 3ccd27d..8d2958d 100644 --- a/src/Kroha/Types.hs +++ b/src/Kroha/Types.hs @@ -1,16 +1,14 @@ -- Copyright (c) 2020 - 2021 Vorotynsky Maxim -{-# LANGUAGE ImplicitParams #-} - module Kroha.Types where -import Data.Graph (Tree(..), Graph(..), path) +import Data.Graph (Graph, path) import Data.Bifunctor (bimap) import Control.Monad (join) import Data.List.Extra (elemIndex) import Data.Either.Extra (maybeToEither) -import Kroha.Ast +import Kroha.Syntax.Syntax import Kroha.Scope import Kroha.Errors @@ -30,6 +28,8 @@ declType :: Declaration d -> TypeName declType (GlobalVariable _ t _ _) = t declType (ConstantVariable _ t _ _) = t declType (ManualVariable _ t _ _) = t +declType (Frame l _ _) = error $ "[Exception]: Unexpected declaration to extract type. \tError location: Frame (" ++ l ++ ")" +declType (ManualFrame l _ _) = error $ "[Exception]: Unexpected declaration to extract type. \tError location: ManualFrame (" ++ l ++ ")" getType :: (?tc :: TypeConfig) => ScopeLink -> Result TypeId @@ -46,7 +46,7 @@ rvalType _ (AsRValue (RegisterLVal reg )) nid = maybeToEither (UnknownRegister r type TypeCast = (TypeId, TypeId) -extractE (a, b) = (\[a, b] -> (a, b)) <$> sequenceErrors JoinedError [a, b] +extractE (a, b) = (\[a', b'] -> (a', b')) <$> sequenceErrors JoinedError [a, b] makeTypeCast :: (?tc :: TypeConfig) => Scope -> NodeId -> (RValue, RValue) -> Result TypeCast makeTypeCast scope nid values = extractE $ bimap find find values @@ -72,5 +72,5 @@ resolve :: TypeConfig -> Program (NodeId, [TypeCast]) -> Result (Program [TypeCa resolve config = processError (map (resolveCast (typeCasts config)) . append) where resolveCast g (nid, c@(f, t)) = if path g f t then Right c else Left (nid, c) typeName (nid, (t1, t2)) = let name typeId = fst $ types config !! typeId in TypeCastError (name t2) (name t1) nid - append (a, l) = fmap ((,) a) l + append (a, l) = fmap (a,) l processError f = sequenceErrors JoinedError . fmap (sequenceErrors (JoinedError . map typeName) . f) diff --git a/src/Main.hs b/src/Main.hs index 66cdb97..0dba7b0 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -1,10 +1,9 @@ module Main where -import Data.Either.Extra (fromEither) import Data.Either (partitionEithers) import Data.List (intercalate) import System.Environment (getArgs) -import System.Exit (exitFailure, exitSuccess) +import System.Exit (exitFailure) import Kroha parse :: [(String, String)] -> IO String diff --git a/test/Spec.hs b/test/Spec.hs index 3e7836a..1e42d83 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -11,6 +11,8 @@ cases = , "examples/ifelse" , "examples/nestedIfs" , "examples/loops" + , "examples/semicolons" + , "examples/elseif" , "errors/scopeErrors" , "errors/typeErrors" ] diff --git a/test/examples/elseif.test.kr b/test/examples/elseif.test.kr new file mode 100644 index 0000000..99f0770 --- /dev/null +++ b/test/examples/elseif.test.kr @@ -0,0 +1,35 @@ +program { + frame sign { + reg x : bx + + if (x > 0, XGTZ) { + x = 2 + } + else if (x == 0, XEZ) { + x = 1 + } + else { + x = 0 + } + !dec bx + } +} +======= nasm ======= +section .text +sign: + cmp bx, 0 + jg XGTZ_begin + cmp bx, 0 + je XEZ_begin + mov bx, 0 + jmp XEZ_end + XEZ_begin: + mov bx, 1 + XEZ_end: + jmp XGTZ_end + XGTZ_begin: + mov bx, 2 + XGTZ_end: + dec bx +leave +ret diff --git a/test/examples/frameVariables.test.kr b/test/examples/frameVariables.test.kr index c09b253..6cf45c9 100644 --- a/test/examples/frameVariables.test.kr +++ b/test/examples/frameVariables.test.kr @@ -1,21 +1,19 @@ -program { - frame register { - reg x : ax - reg y : bx +frame register { + reg x : ax + reg y : bx - x = 5 - y = x - } + x = 5 + y = x +} - frame stackNreg { - var x : int16 - reg y : ax +frame stackNreg { + var x : int16 + reg y : ax - x = 5 - y = x - x = y - y = 6 - } + x = 5 + y = x + x = y + y = 6 } ======= nasm ======= @@ -34,4 +32,3 @@ stackNreg: mov ax, 6 leave ret - diff --git a/test/examples/functionCall.test.kr b/test/examples/functionCall.test.kr index 4f69ceb..9c13773 100644 --- a/test/examples/functionCall.test.kr +++ b/test/examples/functionCall.test.kr @@ -4,7 +4,8 @@ program { } frame callFunction { - call () + call() + call justFunc() !nop } } @@ -18,9 +19,10 @@ ret section .text callFunction: + call justFunc + add sp, 0 call justFunc add sp, 0 nop leave ret - diff --git a/test/examples/functionDeclaration.test.kr b/test/examples/functionDeclaration.test.kr index be28c48..1cbc98a 100644 --- a/test/examples/functionDeclaration.test.kr +++ b/test/examples/functionDeclaration.test.kr @@ -1,11 +1,9 @@ -program { - frame start { - !nop - } +frame start { + !nop +} - manual frame mf { - add ax, dx - } +manual frame mf { + add ax, dx } ======= nasm ======= @@ -18,5 +16,3 @@ ret section .text mf: add ax, dx - - diff --git a/test/examples/if.test.kr b/test/examples/if.test.kr index a16f672..aae3f98 100644 --- a/test/examples/if.test.kr +++ b/test/examples/if.test.kr @@ -1,27 +1,24 @@ -program { - frame if_eq { - reg x : ax - - if (x == 5, A) { - !inc ax - } - - !nop +frame if_eq { + reg x : ax + + if (x == 5, A) { + !inc ax } - frame if_comp { - reg x : ax - - if (x > 5, B) { - !add ax, 5 - } + !nop +} - if (x < 7, C) { - !sub ax, 7 - } +frame if_comp { + reg x : ax - !nop + if (x > 5, B) { + !add ax, 5 } + + if (x < 7, C) + !sub ax, 7 + + !nop } ======= nasm ======= @@ -49,9 +46,8 @@ if_comp: jl C_begin jmp C_end C_begin: - sub ax, 7 + sub ax, 7 C_end: nop leave ret - diff --git a/test/examples/ifelse.test.kr b/test/examples/ifelse.test.kr index fb05315..b739ed4 100644 --- a/test/examples/ifelse.test.kr +++ b/test/examples/ifelse.test.kr @@ -1,29 +1,25 @@ -program { - frame ifelse_eq { - reg x : ax - - if (x == 5, A) { - !dec ax - } - else { - !inc ax - } - - !nop +frame ifelse_eq { + reg x : ax + + if (x == 5, A) { + !dec ax + } + else { + !inc ax } - frame ifelse_comp { - reg x : ax + !nop +} - if (x < 5, B) { - !dec ax - } - else { - !inc ax - } +frame ifelse_comp { + reg x : ax - !nop - } + if (x < 5, B) + !dec ax + else + !inc ax + + !nop } ======= nasm ======= @@ -44,12 +40,11 @@ section .text ifelse_comp: cmp ax, 5 jl B_begin - inc ax + inc ax jmp B_end B_begin: - dec ax + dec ax B_end: nop leave ret - diff --git a/test/examples/loops.test.kr b/test/examples/loops.test.kr index 3143319..f0ef3ca 100644 --- a/test/examples/loops.test.kr +++ b/test/examples/loops.test.kr @@ -1,23 +1,29 @@ -program { - frame infLoop { - loop (IL) { - !inc ax - } +frame infLoop { + loop (IL) { + !inc ax } - frame main { - reg x : ax + loop IL2 + !inc ax +} + +frame main { + reg x : ax - loop (JL) { - !inc ax + loop JL { + !inc ax - if (x > 5, BL) { - break (JL) - } + if (x > 5, BL) { + break (JL) } + } - !nop + loop PL { + !xor ax, ax + break PL } + + !nop } ======= nasm ======= @@ -27,6 +33,10 @@ infLoop: inc ax jmp IL_begin IL_end: + IL2_begin: + inc ax + jmp IL2_begin + IL2_end: leave ret @@ -42,7 +52,11 @@ main: BL_end: jmp JL_begin JL_end: + PL_begin: + xor ax, ax + jmp PL_end + jmp PL_begin + PL_end: nop leave ret - diff --git a/test/examples/nestedIfs.test.kr b/test/examples/nestedIfs.test.kr index 40dca94..eb44850 100644 --- a/test/examples/nestedIfs.test.kr +++ b/test/examples/nestedIfs.test.kr @@ -1,38 +1,36 @@ -program { - frame plot { - reg x : ax - reg y : bx - reg r : dx +frame plot { + reg x : ax + reg y : bx + reg r : dx - if (x > 0, XP) { - if (y > 0, YP) { - r = 1 + if (x > 0, XP) { + if (y > 0, YP) { + r = 1 + } else { + if (y < 0, YP2) { + r = 4 } else { - if (y < 0, YP2) { - r = 4 - } else { - r = 20 - } + r = 20 } } - else { - if (x < 0, XP2) { - if (y > 0, YP3) { - r = 2 - } else { - if (y < 0, YP4) { - r = 3 - } else { - r = 10 - } - } + } + else { + if (x < 0, XP2) { + if (y > 0, YP3) { + r = 2 } else { - if (y == 0, O) { - r = 0 + if (y < 0, YP4) { + r = 3 } else { r = 10 - } + } } + } else { + if (y == 0, O) { + r = 0 + } else { + r = 10 + } } } } @@ -85,4 +83,3 @@ plot: XP_end: leave ret - diff --git a/test/examples/semicolons.test.kr b/test/examples/semicolons.test.kr new file mode 100644 index 0000000..0edb356 --- /dev/null +++ b/test/examples/semicolons.test.kr @@ -0,0 +1,33 @@ +program { + frame main { + var x : int16; + reg y : ax; + + y = x; x = y; + + loop L { + if (x > y, C) { + break L; + } else { + break L + } + } + } +} +======= nasm ======= +section .text +main: + mov ax, [bp - 2] + mov word [bp - 2], ax + L_begin: + cmp word [bp - 2], ax + jg C_begin + jmp L_end + jmp C_end + C_begin: + jmp L_end + C_end: + jmp L_begin + L_end: +leave +ret diff --git a/test/examples/variableDeclarations.test.kr b/test/examples/variableDeclarations.test.kr index f7f5212..b7466dd 100644 --- a/test/examples/variableDeclarations.test.kr +++ b/test/examples/variableDeclarations.test.kr @@ -1,12 +1,12 @@ -program { - var a : int16 = 32 - var b : int8 = 1 - const c : int16 = 32 - const d : int8 = 1 +program; - manual var arr : &int8 { - times 64 db 0 - } +var a : int16 = 32 +var b : int8 = 1 +const c : int16 = 32 +const d : int8 = 1 + +manual var arr : &int8 { + times 64 db 0 } ======= nasm ======= @@ -23,7 +23,5 @@ section .rodata d: db 1 section .data -arr: - times 64 db 0 - - +arr: times 64 db 0 + \ No newline at end of file