From dab6ec91eaabe51b7d5103ba03500513973e7166 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sat, 8 May 2021 19:44:42 +0100 Subject: [PATCH 1/8] Update cabal file, including for LLVM-12. Fix test-linkedlist and Bench.hs --- .gitignore | 1 + bench/Bench.hs | 2 +- mcc.cabal | 198 ++++++++++++++-------------------- tests/pass/test-linkedlist.mc | 4 + 4 files changed, 87 insertions(+), 118 deletions(-) diff --git a/.gitignore b/.gitignore index 49cb2f2..4d5b3fb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ cabal-dev *.chs.h *.dyn_o *.dyn_hi +.hie .hpc .hsenv .cabal-sandbox/ diff --git a/bench/Bench.hs b/bench/Bench.hs index fd13c3d..3547ad6 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -17,7 +17,7 @@ megaparse input = case runParser programP "" input of Left _ -> error "We only like success" Right p -> p -happyparse = alexScanTokens . parse +happyparse = parse . alexScanTokens main = do mcFiles <- findByExtension [".mc"] "tests/pass" diff --git a/mcc.cabal b/mcc.cabal index d92b875..0d2b7ac 100644 --- a/mcc.cabal +++ b/mcc.cabal @@ -1,4 +1,4 @@ -cabal-version: 1.12 +cabal-version: 2.4 -- This file has been generated from package.yaml by hpack version 0.33.0. -- @@ -6,157 +6,121 @@ cabal-version: 1.12 -- -- hash: a168355244c92a5a39b4a5a9f6accd48993a937062b5c7daca3f02e5c8838a2c -name: mcc -version: 0.1.0.0 -synopsis: A microc compiler in Haskell -category: Compilers/Interpreters -homepage: https://github.com/jmorag/mcc#readme -bug-reports: https://github.com/jmorag/mcc/issues -author: Joseph Morag -maintainer: Joseph Morag -license: BSD3 -license-file: LICENSE -build-type: Simple -extra-source-files: - README.md +name: mcc +version: 0.1.0.0 +synopsis: A microc compiler in Haskell +description: A microc compiler in Haskell. +category: Compilers/Interpreters +homepage: https://github.com/jmorag/mcc#readme +bug-reports: https://github.com/jmorag/mcc/issues +author: Joseph Morag +maintainer: Joseph Morag +license: BSD-3-Clause +license-file: LICENSE +build-type: Simple +extra-source-files: README.md source-repository head - type: git + type: git location: https://github.com/jmorag/mcc +common common-options + build-depends: + , base >=4.7 && <5 + , megaparsec + , string-conversions + , text + + ghc-options: + -Wall -Wextra -Wcompat -Widentities -Wincomplete-uni-patterns + -Wincomplete-record-updates -Wredundant-constraints + -Wnoncanonical-monad-instances -fno-warn-name-shadowing + + default-extensions: + LambdaCase + OverloadedStrings + + if impl(ghc >=8.2) + ghc-options: -fhide-source-paths + + if impl(ghc >=8.4) + ghc-options: -Wmissing-export-lists -Wpartial-fields + + if impl(ghc >=8.8) + ghc-options: -Wmissing-deriving-strategies -fwrite-ide-info -hiedir=.hie + + if impl(ghc >=8.10) + ghc-options: -Wunused-packages + + other-modules: Paths_mcc + autogen-modules: Paths_mcc + default-language: Haskell2010 + library + import: common-options exposed-modules: - Microc - Microc.Ast - Microc.Codegen - Microc.Parser.Combinator - Microc.Parser.Generator - Microc.Sast - Microc.Scanner.Combinator - Microc.Scanner.Generator - Microc.Semant - Microc.Semant.Analysis - Microc.Semant.Error - Microc.Toplevel - Microc.Utils - other-modules: - Paths_mcc - hs-source-dirs: - src - default-extensions: OverloadedStrings, LambdaCase - ghc-options: -Wall -fno-warn-name-shadowing -Wcompat -Wincomplete-uni-patterns + Microc + Microc.Ast + Microc.Codegen + Microc.Parser.Combinator + Microc.Parser.Generator + Microc.Sast + Microc.Scanner.Combinator + Microc.Scanner.Generator + Microc.Semant + Microc.Semant.Analysis + Microc.Semant.Error + Microc.Toplevel + Microc.Utils + + hs-source-dirs: src build-depends: - array - , base >=4.7 && <5 + , array , bytestring , containers , directory - , filepath - , llvm-hs >=9 && <10 - , llvm-hs-pretty >=0.9 && <1 - , llvm-hs-pure >=9 && <10 - , megaparsec + , llvm-hs >=9 && <13 + , llvm-hs-pure >=9 && <13 , mtl , parser-combinators , prettyprinter , process - , string-conversions - , text , unix - default-language: Haskell2010 executable mcc - main-is: Main.hs - other-modules: - Paths_mcc - hs-source-dirs: - app - default-extensions: OverloadedStrings, LambdaCase - ghc-options: -Wall -fno-warn-name-shadowing -Wcompat -Wincomplete-uni-patterns + import: common-options + main-is: Main.hs + other-modules: Paths_mcc + hs-source-dirs: app build-depends: - array - , base >=4.7 && <5 - , bytestring - , containers - , directory - , filepath - , llvm-hs >=9 && <10 - , llvm-hs-pretty >=0.9 && <1 - , llvm-hs-pure >=9 && <10 + , llvm-hs-pretty >=0.9 && <13 , mcc - , megaparsec - , mtl , optparse-applicative - , parser-combinators , pretty-simple , prettyprinter - , process - , string-conversions - , text - , unix - default-language: Haskell2010 test-suite testall - type: exitcode-stdio-1.0 - main-is: Testall.hs - other-modules: - Paths_mcc - hs-source-dirs: - tests - default-extensions: OverloadedStrings, LambdaCase - ghc-options: -Wall -fno-warn-name-shadowing -Wcompat -Wincomplete-uni-patterns + import: common-options + type: exitcode-stdio-1.0 + main-is: Testall.hs + other-modules: Paths_mcc + hs-source-dirs: tests build-depends: - array - , base >=4.7 && <5 - , bytestring - , containers - , directory , filepath - , llvm-hs >=9 && <10 - , llvm-hs-pretty >=0.9 && <1 - , llvm-hs-pure >=9 && <10 , mcc - , megaparsec - , mtl - , parser-combinators , prettyprinter - , process - , string-conversions , tasty , tasty-golden , tasty-hunit - , text - , unix - default-language: Haskell2010 benchmark bench - type: exitcode-stdio-1.0 - main-is: Bench.hs - other-modules: - Paths_mcc - hs-source-dirs: - bench - default-extensions: OverloadedStrings, LambdaCase - ghc-options: -Wall -fno-warn-name-shadowing -Wcompat -Wincomplete-uni-patterns + import: common-options + type: exitcode-stdio-1.0 + main-is: Bench.hs + other-modules: Paths_mcc + hs-source-dirs: bench build-depends: - array - , base >=4.7 && <5 - , bytestring - , containers , criterion - , directory , filepath - , llvm-hs >=9 && <10 - , llvm-hs-pretty >=0.9 && <1 - , llvm-hs-pure >=9 && <10 , mcc - , megaparsec - , mtl - , parser-combinators - , prettyprinter - , process - , string-conversions , tasty-golden - , text - , unix - default-language: Haskell2010 diff --git a/tests/pass/test-linkedlist.mc b/tests/pass/test-linkedlist.mc index b1bbc16..1d4918b 100644 --- a/tests/pass/test-linkedlist.mc +++ b/tests/pass/test-linkedlist.mc @@ -33,6 +33,10 @@ int main() { int i; struct List *l; struct List *l2; + + l = NULL; + l2 = NULL; + for (i = 0; i < 20; i = i + 1) { l = cons(i, l); } From 2f986d0c5b13ff5238536214c70fb5f1df1f8365 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sat, 8 May 2021 19:53:29 +0100 Subject: [PATCH 2/8] Minor HLinting --- mcc.cabal | 2 ++ src/Microc/Codegen.hs | 2 +- src/Microc/Parser/Combinator.hs | 2 +- src/Microc/Semant.hs | 2 +- tests/Testall.hs | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mcc.cabal b/mcc.cabal index 0d2b7ac..b2132ba 100644 --- a/mcc.cabal +++ b/mcc.cabal @@ -42,6 +42,8 @@ common common-options if impl(ghc >=8.2) ghc-options: -fhide-source-paths + default-extensions: + DerivingStrategies if impl(ghc >=8.4) ghc-options: -Wmissing-export-lists -Wpartial-fields diff --git a/src/Microc/Codegen.hs b/src/Microc/Codegen.hs index 2fbc5c4..968bb7f 100644 --- a/src/Microc/Codegen.hs +++ b/src/Microc/Codegen.hs @@ -80,7 +80,7 @@ ltypeOfTyp = \case TyFloat -> pure AST.double TyBool -> pure AST.i1 -- (void *) is invalid LLVM - Pointer TyVoid -> pure $ charStar + Pointer TyVoid -> pure charStar -- special case to handle recursively defined structures -- TODO: add real cycle checking so that improperly defined -- recursive types case the compiler to hang forever diff --git a/src/Microc/Parser/Combinator.hs b/src/Microc/Parser/Combinator.hs index 95d1e90..d81fef6 100644 --- a/src/Microc/Parser/Combinator.hs +++ b/src/Microc/Parser/Combinator.hs @@ -23,7 +23,7 @@ opTable = , unary (Unop Not) "!" , unary Deref "*" , unary Addr "&" - , Prefix (try $ Cast <$> (parens typeP)) + , Prefix (try $ Cast <$> parens typeP) ] , [infixR Power "**"] , [infixL Mult "*", infixL Div "/"] diff --git a/src/Microc/Semant.hs b/src/Microc/Semant.hs index beee3d1..3f720b8 100644 --- a/src/Microc/Semant.hs +++ b/src/Microc/Semant.hs @@ -62,7 +62,7 @@ builtIns = M.fromList $ map ] where toFunc (name, tys, retty) = - (name, Function retty name (map (flip Bind "x") tys) [] []) + (name, Function retty name (map (`Bind` "x") tys) [] []) checkExpr :: Expr -> Semant SExpr checkExpr expr = case expr of diff --git a/tests/Testall.hs b/tests/Testall.hs index 5eed16d..d215985 100644 --- a/tests/Testall.hs +++ b/tests/Testall.hs @@ -48,7 +48,7 @@ parsing = do files <- concat <$> mapM (findByExtension [".mc"]) ["tests/pass", "tests/fail"] fmap (testGroup "parsing") $ forM files $ \file -> do input <- T.readFile file - combinator <- pure $ runParser programP file input + let combinator = runParser programP file input generator <- try @IOError . evaluate . parse . alexScanTokens $ cs input pure . testCase file $ case (combinator, generator) of (Right ast, Right ast') -> assertEqual file ast ast' From 7b9aee40cef7f64c4fa005de362fb31e505a7733 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sat, 8 May 2021 22:17:07 +0100 Subject: [PATCH 3/8] Add explict export lists and stock derivings --- app/Main.hs | 4 +- bench/Bench.hs | 22 ++-- mcc.cabal | 2 + src/Microc/Ast.hs | 177 ++++++++++++++++++------------- src/Microc/Codegen.hs | 2 +- src/Microc/Sast.hs | 17 ++- src/Microc/Scanner/Combinator.hs | 29 +++-- src/Microc/Scanner/Generator.x | 2 +- src/Microc/Semant/Analysis.hs | 2 +- src/Microc/Semant/Error.hs | 19 ++-- src/Microc/Toplevel.hs | 2 +- src/Microc/Utils.hs | 4 +- tests/Testall.hs | 2 +- 13 files changed, 179 insertions(+), 105 deletions(-) diff --git a/app/Main.hs b/app/Main.hs index a1a97aa..7d86c36 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,4 +1,4 @@ -module Main where +module Main(main) where import Microc hiding ( Parser ) @@ -14,7 +14,7 @@ import Data.Text.Prettyprint.Doc.Render.Text data Action = Ast | Sast | LLVM | Compile FilePath | Run data ParserType = Combinator | Generator -data Options = Options { action :: Action, infile :: FilePath, parser :: ParserType } +data Options = Options { _action :: Action, _infile :: FilePath, _parser :: ParserType } actionP :: Parser Action actionP = flag' Ast (long "ast" <> short 'a' <> help "Pretty print the ast") diff --git a/bench/Bench.hs b/bench/Bench.hs index 3547ad6..8ca9e21 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -1,24 +1,30 @@ -module Main where +module Main ( main) where + +-- TODO:: incomplete + import Microc -import Data.String.Conversions -import qualified Data.Text.IO as T +-- import Data.String.Conversions +-- import qualified Data.Text.IO as T import Data.Text ( Text - , unpack - ) -import System.FilePath ( takeBaseName - , replaceExtension +-- , unpack ) +-- import System.FilePath ( takeBaseName +-- , replaceExtension +-- ) import Criterion.Main import Test.Tasty.Golden -import Control.Monad +-- import Control.Monad +megaparse :: Text -> Program megaparse input = case runParser programP "" input of Left _ -> error "We only like success" Right p -> p +happyparse :: String -> Program happyparse = parse . alexScanTokens +main :: IO() main = do mcFiles <- findByExtension [".mc"] "tests/pass" defaultMain [] diff --git a/mcc.cabal b/mcc.cabal index b2132ba..b54a8eb 100644 --- a/mcc.cabal +++ b/mcc.cabal @@ -20,6 +20,8 @@ license-file: LICENSE build-type: Simple extra-source-files: README.md +tested-with: GHC ==8.10.4 + source-repository head type: git location: https://github.com/jmorag/mcc diff --git a/src/Microc/Ast.hs b/src/Microc/Ast.hs index 39b36a8..37d8c05 100644 --- a/src/Microc/Ast.hs +++ b/src/Microc/Ast.hs @@ -1,7 +1,18 @@ -module Microc.Ast where +module Microc.Ast + ( Op(..) + , Uop(..) + , Struct(..) + , Type(..) + , Bind(..) + , Expr(..) + , Statement(..) + , Function(..) + , Program(..) + ) where + +import Data.Char ( chr ) import Data.Text ( Text ) import Data.Text.Prettyprint.Doc -import Data.Char ( chr ) data Op = Add | Sub @@ -18,14 +29,17 @@ data Op = Add | Or | BitAnd | BitOr - deriving (Show, Eq) + deriving stock (Show, Eq) data Uop = Neg | Not - deriving (Show, Eq) + deriving stock (Show, Eq) -data Struct = Struct { structName :: Text, structFields :: [Bind] } - deriving (Show, Eq) +data Struct = Struct + { structName :: Text + , structFields :: [Bind] + } + deriving stock (Show, Eq) data Type = Pointer Type | TyInt @@ -34,8 +48,12 @@ data Type = Pointer Type | TyChar | TyVoid | TyStruct Text - deriving (Show, Eq) -data Bind = Bind { bindType :: Type, bindName :: Text } deriving (Show, Eq) + deriving stock (Show, Eq) +data Bind = Bind + { bindType :: Type + , bindName :: Text + } + deriving stock (Show, Eq) data Expr = Literal Int | StrLit Text @@ -54,7 +72,7 @@ data Expr = Literal Int | Assign Expr Expr | Sizeof Type | Noexpr - deriving (Show, Eq) + deriving stock (Show, Eq) data Statement = Expr Expr | Block [Statement] @@ -62,39 +80,40 @@ data Statement = Expr Expr | If Expr Statement Statement | For Expr Expr Expr Statement | While Expr Statement - deriving (Show, Eq) + deriving stock (Show, Eq) data Function = Function - { typ :: Type - , name :: Text + { typ :: Type + , name :: Text , formals :: [Bind] - , locals :: [Bind] - , body :: [Statement] + , locals :: [Bind] + , body :: [Statement] } - deriving (Show, Eq) + deriving stock (Show, Eq) -data Program = Program [Struct] [Bind] [Function] deriving (Eq, Show) +data Program = Program [Struct] [Bind] [Function] + deriving stock (Eq, Show) -------------------------------------------- -- Pretty instances -------------------------------------------- instance Pretty Op where pretty = \case - Add -> "+" - Sub -> "-" - Mult -> "*" - Div -> "/" - Power -> "**" - Equal -> "==" - Neq -> "!=" - Less -> "<" - Leq -> "<=" + Add -> "+" + Sub -> "-" + Mult -> "*" + Div -> "/" + Power -> "**" + Equal -> "==" + Neq -> "!=" + Less -> "<" + Leq -> "<=" Greater -> ">" - Geq -> ">=" - And -> "&&" - Or -> "||" - BitAnd -> "&" - BitOr -> "|" + Geq -> ">=" + And -> "&&" + Or -> "||" + BitAnd -> "&" + BitOr -> "|" instance Pretty Uop where pretty = \case @@ -102,18 +121,24 @@ instance Pretty Uop where Not -> "!" instance Pretty Struct where - pretty (Struct nm binds) = "struct" <+> - pretty nm <+> lbrace <> hardline <> indent 4 (vsep (map (\b -> pretty b <> ";") binds)) - <> hardline <> rbrace <> ";" + pretty (Struct nm binds) = + "struct" + <+> pretty nm + <+> lbrace + <> hardline + <> indent 4 (vsep (map (\b -> pretty b <> ";") binds)) + <> hardline + <> rbrace + <> ";" instance Pretty Type where pretty = \case - TyInt -> "int" - TyBool -> "bool" - TyChar -> "char" - TyFloat -> "float" - TyVoid -> "void" - Pointer t -> pretty t <+> "*" + TyInt -> "int" + TyBool -> "bool" + TyChar -> "char" + TyFloat -> "float" + TyVoid -> "void" + Pointer t -> pretty t <+> "*" TyStruct n -> "struct" <+> pretty n instance Pretty Bind where @@ -121,53 +146,63 @@ instance Pretty Bind where instance Pretty Expr where pretty = \case - Literal i -> pretty i - Fliteral f -> pretty f - CharLit c -> squotes $ pretty (chr c) - StrLit s -> dquotes $ pretty s - BoolLit b -> if b then "true" else "false" - Null -> "NULL" - Id t -> pretty t - Binop op lhs rhs -> hsep [pretty lhs, pretty op, pretty rhs] - Unop op e -> pretty op <> parens (pretty e) - Call f es -> pretty f <> tupled (map pretty es) - Cast t e -> parens (pretty t) <> parens (pretty e) + Literal i -> pretty i + Fliteral f -> pretty f + CharLit c -> squotes $ pretty (chr c) + StrLit s -> dquotes $ pretty s + BoolLit b -> if b then "true" else "false" + Null -> "NULL" + Id t -> pretty t + Binop op lhs rhs -> hsep [pretty lhs, pretty op, pretty rhs] + Unop op e -> pretty op <> parens (pretty e) + Call f es -> pretty f <> tupled (map pretty es) + Cast t e -> parens (pretty t) <> parens (pretty e) Access struct field -> pretty struct <> "." <> pretty field - Assign lhs rhs -> pretty lhs <+> "=" <+> pretty rhs - Deref e -> "*" <> parens (pretty e) - Addr e -> "&" <> parens (pretty e) - Sizeof t -> "sizeof" <> parens (pretty t) - Noexpr -> mempty + Assign lhs rhs -> pretty lhs <+> "=" <+> pretty rhs + Deref e -> "*" <> parens (pretty e) + Addr e -> "&" <> parens (pretty e) + Sizeof t -> "sizeof" <> parens (pretty t) + Noexpr -> mempty instance Pretty Statement where pretty = \case Expr e -> pretty e <> semi - Block ss -> lbrace <> hardline <> indent 4 (vsep (map pretty ss)) - <> hardline <> rbrace + Block ss -> + lbrace + <> hardline + <> indent 4 (vsep (map pretty ss)) + <> hardline + <> rbrace Return e -> "return" <+> pretty e <> semi If pred cons alt -> "if" <+> parens (pretty pred) <+> pretty cons <> prettyAlt - where - prettyAlt = - case alt of - Block [] -> mempty - _ -> hardline <> "else" <+> pretty alt - For init cond inc body -> "for" <+> - encloseSep lparen rparen semi [pretty init, pretty cond, pretty inc] - <+> pretty body + where + prettyAlt = case alt of + Block [] -> mempty + _ -> hardline <> "else" <+> pretty alt + For init cond inc body -> + "for" + <+> encloseSep lparen rparen semi [pretty init, pretty cond, pretty inc] + <+> pretty body While cond body -> "while" <+> parens (pretty cond) <+> pretty body instance Pretty Function where pretty (Function typ name formals locals body) = - pretty typ <+> pretty name <> tupled (map pretty formals) - <> hardline <> lbrace <> hardline <> - indent 4 (hardsep (map decl locals ++ map pretty body)) - <> hardline <> rbrace <> hardline + pretty typ + <+> pretty name + <> tupled (map pretty formals) + <> hardline + <> lbrace + <> hardline + <> indent 4 (hardsep (map decl locals ++ map pretty body)) + <> hardline + <> rbrace + <> hardline instance Pretty Program where - pretty (Program structs binds funcs) = hardsep - (map pretty structs ++ map decl binds ++ map pretty funcs) + pretty (Program structs binds funcs) = + hardsep (map pretty structs ++ map decl binds ++ map pretty funcs) decl :: Pretty a => a -> Doc ann decl bind = pretty bind <> semi diff --git a/src/Microc/Codegen.hs b/src/Microc/Codegen.hs index 968bb7f..c820c4b 100644 --- a/src/Microc/Codegen.hs +++ b/src/Microc/Codegen.hs @@ -50,7 +50,7 @@ data Env = Env { operands :: M.Map Text Operand , structs :: [ Struct ] , strings :: M.Map Text Operand } - deriving (Eq, Show) + deriving stock (Eq, Show) -- LLVM and Codegen type synonyms allow us to emit module definitions and basic -- block instructions at the top level without being forced to pass explicit diff --git a/src/Microc/Sast.hs b/src/Microc/Sast.hs index 22f45d0..b0c086b 100644 --- a/src/Microc/Sast.hs +++ b/src/Microc/Sast.hs @@ -1,4 +1,11 @@ -module Microc.Sast where +module Microc.Sast + ( SExpr + , SExpr'(..) + , LValue(..) + , SStatement(..) + , SFunction(..) + , SProgram + ) where import Microc.Ast import Data.Text ( Text ) @@ -20,13 +27,13 @@ data SExpr' = | SAddr LValue | SSizeof Type | SNoexpr - deriving (Show, Eq) + deriving stock (Show, Eq) -- | LValues are the class of assignable expressions that can appear -- on the Left side on the '=' operator and that can have their addresses -- taken. data LValue = SDeref SExpr | SAccess LValue Int | SId Text - deriving (Show, Eq) + deriving stock (Show, Eq) data SStatement = SExpr SExpr @@ -34,7 +41,7 @@ data SStatement = | SReturn SExpr | SIf SExpr SStatement SStatement | SDoWhile SExpr SStatement - deriving (Show, Eq) + deriving stock (Show, Eq) data SFunction = SFunction { styp :: Type @@ -43,6 +50,6 @@ data SFunction = SFunction , slocals :: [Bind] , sbody :: SStatement } - deriving (Show, Eq) + deriving stock (Show, Eq) type SProgram = ([Struct], [Bind], [SFunction]) diff --git a/src/Microc/Scanner/Combinator.hs b/src/Microc/Scanner/Combinator.hs index dc3ace6..31c19ea 100644 --- a/src/Microc/Scanner/Combinator.hs +++ b/src/Microc/Scanner/Combinator.hs @@ -1,14 +1,31 @@ -module Microc.Scanner.Combinator where +module Microc.Scanner.Combinator + ( Parser + , braces + , charlit + , comma + , float + , identifier + , int + , lexeme + , parens + , rword + , rws + , sc + , semi + , star + , strlit + , symbol + ) where -import Data.Void +import Control.Monad ( void ) import Data.Char +import Data.String.Conversions +import Data.Text ( Text ) +import qualified Data.Text as T +import Data.Void import Text.Megaparsec import Text.Megaparsec.Char import qualified Text.Megaparsec.Char.Lexer as L -import Data.Text ( Text ) -import qualified Data.Text as T -import Control.Monad ( void ) -import Data.String.Conversions type Parser = Parsec Void Text diff --git a/src/Microc/Scanner/Generator.x b/src/Microc/Scanner/Generator.x index 0012931..6f4d54e 100644 --- a/src/Microc/Scanner/Generator.x +++ b/src/Microc/Scanner/Generator.x @@ -1,5 +1,5 @@ { -module Microc.Scanner.Generator where +module Microc.Scanner.Generator ( alexScanTokens, Lexeme(..) ) where import Microc.Ast } diff --git a/src/Microc/Semant/Analysis.hs b/src/Microc/Semant/Analysis.hs index 455681e..f1cd2dd 100644 --- a/src/Microc/Semant/Analysis.hs +++ b/src/Microc/Semant/Analysis.hs @@ -1,4 +1,4 @@ -module Microc.Semant.Analysis where +module Microc.Semant.Analysis ( genCFG, validate ) where import Microc.Sast diff --git a/src/Microc/Semant/Error.hs b/src/Microc/Semant/Error.hs index dc6d095..4493edd 100644 --- a/src/Microc/Semant/Error.hs +++ b/src/Microc/Semant/Error.hs @@ -1,11 +1,18 @@ -module Microc.Semant.Error where +module Microc.Semant.Error + ( BindingKind (..), + BindingLoc (..), + SemantError (..), + SymbolKind (..), + VarKind (..), + ) +where import Microc.Ast import Data.Text ( Text ) import Data.Text.Prettyprint.Doc type Name = Text -data BindingLoc = F Function | S Struct | Toplevel deriving Show +data BindingLoc = F Function | S Struct | Toplevel deriving stock Show data SemantError = IllegalBinding Name BindingKind VarKind BindingLoc | UndefinedSymbol Name SymbolKind Expr @@ -18,12 +25,12 @@ data SemantError = | AssignmentError { lhs :: Expr, rhs :: Expr } | AccessError { struct :: Expr, field :: Expr } | DeadCode Statement -- ^ For statements in a block following a return - deriving (Show) + deriving stock (Show) -data BindingKind = Duplicate | Void deriving (Show) -data SymbolKind = Var | Func deriving (Show) +data BindingKind = Duplicate | Void deriving stock (Show) +data SymbolKind = Var | Func deriving stock (Show) -data VarKind = Global | Formal | Local | StructField deriving (Show, Eq, Ord) +data VarKind = Global | Formal | Local | StructField deriving stock (Show, Eq, Ord) instance Pretty VarKind where pretty = unsafeViaShow diff --git a/src/Microc/Toplevel.hs b/src/Microc/Toplevel.hs index 462581a..0483981 100644 --- a/src/Microc/Toplevel.hs +++ b/src/Microc/Toplevel.hs @@ -1,4 +1,4 @@ -module Microc.Toplevel where +module Microc.Toplevel ( compile, run ) where import LLVM.AST diff --git a/src/Microc/Utils.hs b/src/Microc/Utils.hs index 601dbf8..f42f09b 100644 --- a/src/Microc/Utils.hs +++ b/src/Microc/Utils.hs @@ -1,4 +1,4 @@ -module Microc.Utils where +module Microc.Utils( locally ) where import Control.Monad.State @@ -7,4 +7,4 @@ locally computation = do oldState <- get result <- computation put oldState - return result \ No newline at end of file + return result diff --git a/tests/Testall.hs b/tests/Testall.hs index d215985..81f3a7d 100644 --- a/tests/Testall.hs +++ b/tests/Testall.hs @@ -1,5 +1,5 @@ {-# LANGUAGE TypeApplications #-} -module Main where +module Main ( main) where import Test.Tasty ( defaultMain , TestTree From 1f56a22eb8cace6e0458dccca5f8715137c04fbc Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sun, 9 May 2021 00:32:46 +0100 Subject: [PATCH 4/8] Add some StrictData annotations --- app/Main.hs | 1 + mcc.cabal | 5 +++++ src/Microc/Ast.hs | 1 + src/Microc/Codegen.hs | 1 + src/Microc/Sast.hs | 1 + src/Microc/Semant.hs | 1 + src/Microc/Semant/Analysis.hs | 1 + src/Microc/Semant/Error.hs | 1 + 8 files changed, 12 insertions(+) diff --git a/app/Main.hs b/app/Main.hs index 7d86c36..980b7cc 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,3 +1,4 @@ + {-# LANGUAGE StrictData #-} module Main(main) where import Microc hiding ( Parser ) diff --git a/mcc.cabal b/mcc.cabal index b54a8eb..89a1bf8 100644 --- a/mcc.cabal +++ b/mcc.cabal @@ -102,6 +102,7 @@ executable mcc , optparse-applicative , pretty-simple , prettyprinter +-- ghc-options: -threaded -rtsopts -with-rtsopts=-N test-suite testall import: common-options @@ -116,6 +117,9 @@ test-suite testall , tasty , tasty-golden , tasty-hunit +-- The following breaks the running of the test suite by changing the current +-- working directory +-- ghc-options: -threaded -rtsopts -with-rtsopts=-N benchmark bench import: common-options @@ -128,3 +132,4 @@ benchmark bench , filepath , mcc , tasty-golden +-- ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/src/Microc/Ast.hs b/src/Microc/Ast.hs index 37d8c05..4bf808f 100644 --- a/src/Microc/Ast.hs +++ b/src/Microc/Ast.hs @@ -1,3 +1,4 @@ + {-# LANGUAGE StrictData #-} module Microc.Ast ( Op(..) , Uop(..) diff --git a/src/Microc/Codegen.hs b/src/Microc/Codegen.hs index c820c4b..b4f03a2 100644 --- a/src/Microc/Codegen.hs +++ b/src/Microc/Codegen.hs @@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Microc.Codegen ( codegenProgram diff --git a/src/Microc/Sast.hs b/src/Microc/Sast.hs index b0c086b..4abc84c 100644 --- a/src/Microc/Sast.hs +++ b/src/Microc/Sast.hs @@ -1,3 +1,4 @@ + {-# LANGUAGE StrictData #-} module Microc.Sast ( SExpr , SExpr'(..) diff --git a/src/Microc/Semant.hs b/src/Microc/Semant.hs index 3f720b8..e74de0e 100644 --- a/src/Microc/Semant.hs +++ b/src/Microc/Semant.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} module Microc.Semant ( checkProgram diff --git a/src/Microc/Semant/Analysis.hs b/src/Microc/Semant/Analysis.hs index f1cd2dd..bb63ed1 100644 --- a/src/Microc/Semant/Analysis.hs +++ b/src/Microc/Semant/Analysis.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE StrictData #-} module Microc.Semant.Analysis ( genCFG, validate ) where import Microc.Sast diff --git a/src/Microc/Semant/Error.hs b/src/Microc/Semant/Error.hs index 4493edd..d00472e 100644 --- a/src/Microc/Semant/Error.hs +++ b/src/Microc/Semant/Error.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE StrictData #-} module Microc.Semant.Error ( BindingKind (..), BindingLoc (..), From f3575e6d8737c0c64a1544608c0edeab031bd175 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sun, 9 May 2021 00:50:39 +0100 Subject: [PATCH 5/8] Add some workflows --- .github/workflows/haskell-ci.yml | 168 +++++++++++++++++++++++++++++++ .github/workflows/haskell.yml | 39 +++++++ .travis.yml | 140 +++++++++++++++++++++++++- 3 files changed, 344 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/haskell-ci.yml create mode 100644 .github/workflows/haskell.yml diff --git a/.github/workflows/haskell-ci.yml b/.github/workflows/haskell-ci.yml new file mode 100644 index 0000000..006f04e --- /dev/null +++ b/.github/workflows/haskell-ci.yml @@ -0,0 +1,168 @@ +# This GitHub workflow config has been generated by a script via +# +# haskell-ci 'github' 'mcc.cabal' +# +# To regenerate the script (for example after adjusting tested-with) run +# +# haskell-ci regenerate +# +# For more information, see https://github.com/haskell-CI/haskell-ci +# +# version: 0.13.20210525 +# +# REGENDATA ("0.13.20210525",["github","mcc.cabal"]) +# +name: Haskell-CI +on: + - push + - pull_request +jobs: + linux: + name: Haskell-CI - Linux - ${{ matrix.compiler }} + runs-on: ubuntu-18.04 + container: + image: buildpack-deps:bionic + continue-on-error: ${{ matrix.allow-failure }} + strategy: + matrix: + include: + - compiler: ghc-8.10.4 + allow-failure: false + fail-fast: false + steps: + - name: apt + run: | + apt-get update + apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common + apt-add-repository -y 'ppa:hvr/ghc' + apt-get update + apt-get install -y $CC cabal-install-3.4 + env: + CC: ${{ matrix.compiler }} + - name: Set PATH and environment variables + run: | + echo "$HOME/.cabal/bin" >> $GITHUB_PATH + echo "LANG=C.UTF-8" >> $GITHUB_ENV + echo "CABAL_DIR=$HOME/.cabal" >> $GITHUB_ENV + echo "CABAL_CONFIG=$HOME/.cabal/config" >> $GITHUB_ENV + HCDIR=$(echo "/opt/$CC" | sed 's/-/\//') + HCNAME=ghc + HC=$HCDIR/bin/$HCNAME + echo "HC=$HC" >> $GITHUB_ENV + echo "HCPKG=$HCDIR/bin/$HCNAME-pkg" >> $GITHUB_ENV + echo "HADDOCK=$HCDIR/bin/haddock" >> $GITHUB_ENV + echo "CABAL=/opt/cabal/3.4/bin/cabal -vnormal+nowrap" >> $GITHUB_ENV + HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))') + echo "HCNUMVER=$HCNUMVER" >> $GITHUB_ENV + echo "ARG_TESTS=--enable-tests" >> $GITHUB_ENV + echo "ARG_BENCH=--enable-benchmarks" >> $GITHUB_ENV + echo "HEADHACKAGE=false" >> $GITHUB_ENV + echo "ARG_COMPILER=--$HCNAME --with-compiler=$HC" >> $GITHUB_ENV + echo "GHCJSARITH=0" >> $GITHUB_ENV + env: + CC: ${{ matrix.compiler }} + - name: env + run: | + env + - name: write cabal config + run: | + mkdir -p $CABAL_DIR + cat >> $CABAL_CONFIG < cabal-plan.xz + echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c - + xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan + rm -f cabal-plan.xz + chmod a+x $HOME/.cabal/bin/cabal-plan + cabal-plan --version + - name: checkout + uses: actions/checkout@v2 + with: + path: source + - name: initial cabal.project for sdist + run: | + touch cabal.project + echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project + cat cabal.project + - name: sdist + run: | + mkdir -p sdist + $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist + - name: unpack + run: | + mkdir -p unpacked + find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \; + - name: generate cabal.project + run: | + PKGDIR_mcc="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/mcc-[0-9.]*')" + echo "PKGDIR_mcc=${PKGDIR_mcc}" >> $GITHUB_ENV + touch cabal.project + touch cabal.project.local + echo "packages: ${PKGDIR_mcc}" >> cabal.project + echo "package mcc" >> cabal.project + echo " ghc-options: -Werror=missing-methods" >> cabal.project + cat >> cabal.project <> cabal.project.local + cat cabal.project + cat cabal.project.local + - name: dump install plan + run: | + $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all + cabal-plan + - name: cache + uses: actions/cache@v2 + with: + key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }} + path: ~/.cabal/store + restore-keys: ${{ runner.os }}-${{ matrix.compiler }}- + - name: install dependencies + run: | + $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all + $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all + - name: build w/o tests + run: | + $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all + - name: build + run: | + $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always + - name: tests + run: | + $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct + - name: cabal check + run: | + cd ${PKGDIR_mcc} || false + ${CABAL} -vnormal check + - name: haddock + run: | + $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all + - name: unconstrained build + run: | + rm -f cabal.project.local + $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml new file mode 100644 index 0000000..12cf4c6 --- /dev/null +++ b/.github/workflows/haskell.yml @@ -0,0 +1,39 @@ +name: Haskell CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-haskell@v1 + with: + ghc-version: latest + cabal-version: latest + + - name: Cache + uses: actions/cache@v1 + env: + cache-name: cache-cabal + with: + path: ~/.cabal + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: Install dependencies + run: | + cabal update + cabal build --only-dependencies --enable-tests --enable-benchmarks + - name: Build + run: cabal build --enable-tests --enable-benchmarks all + - name: Run tests + run: cabal test all diff --git a/.travis.yml b/.travis.yml index 409541e..47c5a5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,137 @@ -language: nix -nix: 2.3.2 -script: nix-shell --pure --run 'cabal new-test' +# This Travis job script has been generated by a script via +# +# haskell-ci 'travis' 'mcc.cabal' +# +# To regenerate the script (for example after adjusting tested-with) run +# +# haskell-ci regenerate +# +# For more information, see https://github.com/haskell-CI/haskell-ci +# +# version: 0.13.20210525 +# +version: ~> 1.0 +language: c +os: linux +dist: bionic +git: + # whether to recursively clone submodules + submodules: false +cache: + directories: + - $HOME/.cabal/packages + - $HOME/.cabal/store + - $HOME/.hlint +before_cache: + - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log + # remove files that are regenerated by 'cabal update' + - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.* + - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar + - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx + - rm -rfv $CABALHOME/packages/head.hackage +jobs: + include: + - compiler: ghc-8.10.4 + addons: {"apt":{"packages":["ghc-8.10.4","cabal-install-3.4"],"sources":[{"key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286","sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu bionic main"}]}} + os: linux +before_install: + - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//') + - WITHCOMPILER="-w $HC" + - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//') + - HCPKG="$HC-pkg" + - unset CC + - CABAL=/opt/ghc/bin/cabal + - CABALHOME=$HOME/.cabal + - export PATH="$CABALHOME/bin:$PATH" + - TOP=$(pwd) + - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')" + - echo $HCNUMVER + - CABAL="$CABAL -vnormal+nowrap" + - set -o pipefail + - TEST=--enable-tests + - BENCH=--enable-benchmarks + - HEADHACKAGE=false + - rm -f $CABALHOME/config + - | + echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config + echo "remote-build-reporting: anonymous" >> $CABALHOME/config + echo "write-ghc-environment-files: never" >> $CABALHOME/config + echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config + echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config + echo "world-file: $CABALHOME/world" >> $CABALHOME/config + echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config + echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config + echo "installdir: $CABALHOME/bin" >> $CABALHOME/config + echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config + echo "store-dir: $CABALHOME/store" >> $CABALHOME/config + echo "install-dirs user" >> $CABALHOME/config + echo " prefix: $CABALHOME" >> $CABALHOME/config + echo "repository hackage.haskell.org" >> $CABALHOME/config + echo " url: http://hackage.haskell.org/" >> $CABALHOME/config +install: + - ${CABAL} --version + - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" + - | + echo "program-default-options" >> $CABALHOME/config + echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config + - cat $CABALHOME/config + - rm -fv cabal.project cabal.project.local cabal.project.freeze + - travis_retry ${CABAL} v2-update -v + # Generate cabal.project + - rm -rf cabal.project cabal.project.local cabal.project.freeze + - touch cabal.project + - | + echo "packages: ." >> cabal.project + - echo 'package mcc' >> cabal.project + - "echo ' ghc-options: -Werror=missing-methods' >> cabal.project" + - | + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(mcc)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" + - cat cabal.project || true + - cat cabal.project.local || true + - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi + - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} + - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'" + - rm cabal.project.freeze + - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all + - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all +script: + - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) + # Packaging... + - ${CABAL} v2-sdist all + # Unpacking... + - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/ + - cd ${DISTDIR} || false + - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \; + - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \; + - PKGDIR_mcc="$(find . -maxdepth 1 -type d -regex '.*/mcc-[0-9.]*')" + # Generate cabal.project + - rm -rf cabal.project cabal.project.local cabal.project.freeze + - touch cabal.project + - | + echo "packages: ${PKGDIR_mcc}" >> cabal.project + - echo 'package mcc' >> cabal.project + - "echo ' ghc-options: -Werror=missing-methods' >> cabal.project" + - | + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(mcc)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done" + - cat cabal.project || true + - cat cabal.project.local || true + # Building... + # this builds all libraries and executables (without tests/benchmarks) + - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all + # Building with tests and benchmarks... + # build & run tests, build benchmarks + - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all --write-ghc-environment-files=always + # Testing... + - ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all --test-show-details=direct + # cabal check... + - (cd ${PKGDIR_mcc} && ${CABAL} -vnormal check) + # haddock... + - ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all + # Building without installed constraints for packages in global-db... + - rm -f cabal.project.local + - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all + +# REGENDATA ("0.13.20210525",["travis","mcc.cabal"]) +# EOF From df647ceebe1022154fabec4f3ba174602eaafda6 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sun, 9 May 2021 13:07:27 +0100 Subject: [PATCH 6/8] another explicit export list --- src/Microc/Utils.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microc/Utils.hs b/src/Microc/Utils.hs index f42f09b..6eec46e 100644 --- a/src/Microc/Utils.hs +++ b/src/Microc/Utils.hs @@ -1,4 +1,4 @@ -module Microc.Utils( locally ) where +module Microc.Utils ( locally ) where import Control.Monad.State From 586e89b544d9d1260a80affb5036406e9849b132 Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sun, 9 May 2021 14:17:11 +0100 Subject: [PATCH 7/8] Add installation instructions to README.md --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4456628..cc6e96a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,120 @@ # mcc + Another MicroC compiler. This used to be a fork of the reference compiler for Columbia's _Programming Languages and Translators_ course, but has since grown to include very important C features such as pointers and structs. The original can be found at https://github.com/cwabbott0/microc-llvm. Students in the class are allowed to write the compiler for their own final semester project in OCaml or Haskell, but since the provided reference compiler is in OCaml, almost no one chooses Haskell. This project is an attempt to remedy that situation and open the possibility of using Haskell to more students. The `compat` branch of the repo has only features that are present in the original repo, whereas master has some additions, such as pointers, structs, bitwise operators and an exponentiation operator that demonstrates how to link llvm intrinsic functions. For comparison, two separate parsers are provided, one recursive descent parser implemented with [Megaparsec](https://hackage.haskell.org/package/megaparsec) and one more classical LR-style parser implemented with [Alex](https://hackage.haskell.org/package/alex) and [Happy](https://hackage.haskell.org/package/happy), Haskell's analogues to the venerable lex and yacc. The test suite checks that they return identical parse trees for identical valid source files, although for files which fail to parse correctly, they do not emit the same error messages. ## Setup -*Note*: As of March 2020, the recommended setup for hacking on `mcc` is to just use [nix](https://nixos.org/nix/download.html). Running `nix-shell --pure` at the root of the project _should_ drop you into a shell that has the right versions of ghc, llvm, clang, and all the libraries that `mcc` depends on. The `default.nix` file included is pinned to a particular nixpkgs commit, so hopefully this will continue to be true in perpetuity. To run the test suite, `nix-shell --pure --run 'cabal new-test'` should do. Using stack to build `mcc` is no longer supported, as I don't use it anymore. The legacy instructions below are included for posterity. + +_Note_: As of March 2020, the recommended setup for hacking on `mcc` is to just use [nix](https://nixos.org/nix/download.html). Running `nix-shell --pure` at the root of the project _should_ drop you into a shell that has the right versions of ghc, llvm, clang, and all the libraries that `mcc` depends on. The `default.nix` file included is pinned to a particular nixpkgs commit, so hopefully this will continue to be true in perpetuity. To run the test suite, `nix-shell --pure --run 'cabal new-test'` should do. Using stack to build `mcc` is no longer supported, as I don't use it anymore. The legacy instructions below are included for posterity. + +### Manual installation + +As an alternative to using `nix`, you can use the following recipe: + +1. Assuming that you have `ghc` and `cabal` installed (at least versions 8.8 and 3.2, recommended), install `alex` and `happy`: + +```sh +> cabal update +> cabal install alex happy +``` + +and update `~/.cabal/config` to refer to them. For example: + +``` + alex-location: /Users/xxx/.cabal/bin/alex + happy-location: /Users/xxx/.cabal/bin/happy +``` + +2. Install `llvm`, which includes `llc`, `clang`, and a host of other compiler tools. + Go to http://releases.llvm.org/download.html for further information. There are + also some more [detailed instructions](https://github.com/llvm-hs/llvm-hs#installing-llvm). + +For macOS, for example, use `brew install llvm` and add `/usr/local/opt/llvm/bin` to your `$PATH`. + +3. Check that + +```sh +> llvm-config --libs +-lLLVM-12 +``` + +works. You may need to create the symlinks described at step 5 in the +more [detailed instructions](https://github.com/llvm-hs/llvm-hs#installing-llvm) on how +to install llvm. + +Note that unlike the OCaml version of MicroC, this project requires a more cecent version of LLVM. +(These instauctions use LLVM 12.) + +4. Pull down the necessary sources and make sure that you have the branches + that correspond to the version of `llvm` that you are using checked out: + +```sh +> mkdir myproject +> cd myproject +> git clone https://github.com/llvm-hs/llvm-hs.git +> git checkout llvm-12 +> git clone https://github.com/llvm-hs/llvm-hs-pretty.git +> git checkout llvm-12 +> git clone https://github.com/jmorag/mcc.git +> git checkout llvm-12 +``` + +5. Create a `cabal.project` file in `myproject` (to ensure that you are using the right version + of `llvm-hs`. (Otherwise you risk using the hackage version, which may be + problematic) + +``` +packages: ./llvm-hs-pretty + ./llvm-hs/llvm-hs-pure + ./llvm-hs/llvm-hs + ./mcc +``` + +6. Build and test `mcc`: + +```sh +> cd mcc +> cabal build +> cabal test +``` + +7. You can either install `mcc` to your `~/.cabal/bin` by running `cabal install exe:mcc` or you can `cabal run mcc` from the `mcc` folder: + +```sh +❯ cabal run mcc -- --help +Up to date +Usage: mcc [(-a|--ast) | (-s|--sast) | (-l|--llvm) | (-c|--compile) [-o FILE]] + FILE [--combinator | (-g|--generator)] + Run the mcc compiler on the given file. Passing no flags will compile the + file, execute it, and print the output. + +Available options: + -h,--help Show this help text + -a,--ast Pretty print the ast + -s,--sast Pretty print the sast + -l,--llvm Pretty print the generated llvm + -c,--compile Compile to an executable + FILE Source file + --combinator Use the megaparsec parser implementation (default). + -g,--generator Use alex and happy to parse. + +> cabal run mcc -- tests/pass/test-add1.mc +Up to date +42 +``` + ### Legacy Setup + `mcc` requires the `stack` package manager for Haskell. To install `stack`, see https://docs.haskellstack.org/en/stable/README/. It also requires that `clang` and `llc` binaries be available in your PATH. There are two options to ensure that these will be available: + 1. **Use Nix**: -Using `stack`'s `nix` integration will set up a local environment with the correct versions of `clang` and `llc` installed and added to the PATH. To acquire nix, follow the installation [instructions](https://nixos.org/nix/download.html). Nix is enabled by default, so simply running `stack test` should work if everything is nix and stack are installed correctly. + Using `stack`'s `nix` integration will set up a local environment with the correct versions of `clang` and `llc` installed and added to the PATH. To acquire nix, follow the installation [instructions](https://nixos.org/nix/download.html). Nix is enabled by default, so simply running `stack test` should work if everything is nix and stack are installed correctly. 2. **Manual Installation**: -To install the `llvm` toolchain, which includes `llc`, `clang`, and a host of other compiler tools, go to http://releases.llvm.org/download.html#6.0.1. For macOS, the maintainers of the Haskell-LLVM bindings provide a brew tap, so running `brew install llvm-hs/llvm/llvm-6.0` and adding `/usr/local/opt/llvm/bin` to your `$PATH` should be sufficient. They also provide more [detailed instructions](https://github.com/llvm-hs/llvm-hs#installing-llvm) on how to install llvm on other platforms. Note that unlike the OCaml version of MicroC, this project requires LLVM 6, not LLVM 3.4. To use stack commands without nix integration, pass the `--no-nix` flag to them. + To install the `llvm` toolchain, which includes `llc`, `clang`, and a host of other compiler tools, go to http://releases.llvm.org/download.html#6.0.1. For macOS, the maintainers of the Haskell-LLVM bindings provide a brew tap, so running `brew install llvm-hs/llvm/llvm-6.0` and adding `/usr/local/opt/llvm/bin` to your `$PATH` should be sufficient. They also provide more [detailed instructions](https://github.com/llvm-hs/llvm-hs#installing-llvm) on how to install llvm on other platforms. Note that unlike the OCaml version of MicroC, this project requires LLVM 6, not LLVM 3.4. To use stack commands without nix integration, pass the `--no-nix` flag to them. ### Running and testing the compiler + Once everything is installed, to run the compiler's test suite, navigate to the project home directory and run `stack test`. To build compiler executable, run `stack build mcc`. Remember to add `--no-nix` to these commands if you would like to disable `nix` integration. ## Questions, contributions + Please don't hesitate to open an issue or pull request if you don't understand something in here or you see something that could be improved! Once most of the functionality is complete, I'm planning on releasing a detailed walkthrough on the whole compilation process in a series of posts, but until then, documentation is very sparse. From c53a7ce046448f34ea8f3d57e4649fbc9db8e68a Mon Sep 17 00:00:00 2001 From: jrp2014 Date: Sun, 9 May 2021 14:19:27 +0100 Subject: [PATCH 8/8] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc6e96a..31c1cca 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ more [detailed instructions](https://github.com/llvm-hs/llvm-hs#installing-llvm) to install llvm. Note that unlike the OCaml version of MicroC, this project requires a more cecent version of LLVM. -(These instauctions use LLVM 12.) +(These instructions use LLVM 12.) 4. Pull down the necessary sources and make sure that you have the branches that correspond to the version of `llvm` that you are using checked out: