Compare commits

...

23 Commits

Author SHA1 Message Date
bd7f614d23 Add GHC version info 2024-12-11 17:40:15 -05:00
f4906899ee Add instructions for running tests 2024-12-11 17:35:44 -05:00
d66f1e1401 Add QuickCheck print/parse tests 2024-12-11 17:34:52 -05:00
c47f713a0d Fix enum and struct field printing 2024-12-11 17:34:15 -05:00
c1fd18d525 Include "fn" in function pretty print 2024-12-11 17:19:32 -05:00
fd16a12e7c Simplify assignment op[s 2024-12-11 16:27:17 -05:00
b013ba0e55 Add test code 2024-12-11 16:26:57 -05:00
3d17813eb4 Implement early version of CodeGen 2024-12-11 16:11:05 -05:00
5a63229e74 Use TAst in CodeGen 2024-12-11 15:58:59 -05:00
d53362f882 Implement conversion from Ast to TAst 2024-12-11 15:58:01 -05:00
fe335fa16e Create TAst 2024-12-11 15:57:23 -05:00
24cc7d08d9 Add README with usage instructions 2024-12-09 21:58:47 -05:00
f990373f22 Remove parenthesis from while parsing 2024-12-09 21:44:05 -05:00
891cd41e46 Add shell.nix file 2024-12-09 21:43:28 -05:00
afe6b6dd59 Add mtl package 2024-12-09 21:42:41 -05:00
5d9b956883 Call codegen and write LLVM output to output file 2024-12-09 20:30:01 -05:00
892658de78 Create CodeGen module 2024-12-09 20:29:04 -05:00
898160d611 Add string-conversions package 2024-12-09 20:23:56 -05:00
2819b7fc57 Take in input/output file args 2024-12-09 20:19:31 -05:00
37cf2fe339 Add llvm-hs-pretty package
Fix prettyprinter version conflict
2024-12-09 20:09:13 -05:00
d558831984 Add output files to gitignore 2024-12-09 20:03:56 -05:00
6a9f272cac Fixed Parser issues and simplified AST 2024-11-21 19:08:50 -05:00
aa48976e31 Implemented Basic Parser 2024-11-14 14:05:47 -05:00
21 changed files with 1547 additions and 3 deletions

4
.gitignore vendored
View File

@ -21,3 +21,7 @@ cabal.project.local
cabal.project.local~
.HTF/
.ghc.environment.*
# Windows12 output files
*.ll
*.s

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# Windows12
A C-like compiled programming language implemented in Haskell.
## Usage
Note: You must have version 9.0 of the Haskell compiler GHC installed. Otherwise
Cabal will not be able to obtain the necessary dependencies.
It is recommended to use the provided `shell.nix` file to create the proper
environment. Alternatively, you can run the project from the supplied
binaries in the [releases](https://gitea.mregirouard.com/eta357/Windows-12-Compiler/releases)
section.
```shell
cabal run windows12 <input file> out.ll
llc out.ll -o out.s
gcc out.s -o out
./out
```
## Running Tests
```shell
cabal run windows12-qc
```

7
cabal.project Normal file
View File

@ -0,0 +1,7 @@
-- Needed to get a working version of llvm-hs-pretty
-- The one on hackage is broken with this version of GHC
source-repository-package
type: git
location: https://github.com/rumkeller/llvm-hs-pretty.git
packages: ./windows12.cabal

10
shell.nix Normal file
View File

@ -0,0 +1,10 @@
let
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
buildInputs = [
pkgs.haskell.compiler.ghc90
pkgs.cabal-install
pkgs.llvm_18
];
}

View File

@ -1,4 +1,33 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Text.IO as T
import qualified Data.Text.Lazy.IO as TL
import Data.Text.Lazy (toStrict, unpack)
import Data.String.Conversions (cs)
import Data.Text.Prettyprint.Doc (pretty)
import Text.Megaparsec (parse)
import Windows12.Parser (programP)
import System.Environment (getArgs)
import LLVM.Pretty
import Windows12.Ast
import Windows12.Semant (convert)
import Windows12.CodeGen (codegen)
main :: IO ()
main = putStrLn "Hello, Haskell!"
main = do
args <- getArgs
if length args /= 2
then putStrLn "Usage: windows12 <input file> <output file>"
else do
let [inputFile, outputFile] = args
test <- T.readFile inputFile
case parse programP inputFile test of
Left err -> print err
Right ast ->
case convert ast of
Left err -> putStrLn err
Right ast -> TL.writeFile outputFile (ppllvm (codegen (cs inputFile) ast))

94
src/QuickCheckTests.hs Normal file
View File

@ -0,0 +1,94 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Test.QuickCheck
import Windows12.Ast
import Windows12.Parser (programP)
import Text.Megaparsec (parse)
import Data.String.Conversions (cs)
import Data.Text
import Data.Text.Internal
import Data.Text.Prettyprint.Doc (pretty)
main :: IO ()
main = quickCheck prop_print_parse
-- Ensure that pretty-printing and parsing are inverses
prop_print_parse :: Program -> Bool
prop_print_parse p = Right p == (parse programP "" (cs (show (pretty p))))
instance Arbitrary Data.Text.Internal.Text where
arbitrary = listOf1 (elements ['a'..'z']) >>= return . Data.Text.pack
instance Arbitrary BinOp where
arbitrary = elements [Add, Sub, Mul, Div, Mod, Eq, Ne, Lt, Gt, Le, Ge, And, Or,
BitAnd, BitOr, BitXor, ShiftL, ShiftR]
instance Arbitrary UnOp where
arbitrary = elements [Neg, Not, BitNot]
instance Arbitrary AssignOp where
arbitrary = elements [BaseAssign, AddAssign, SubAssign, MulAssign, DivAssign,
ModAssign, BitAndAssign, BitOrAssign, BitXorAssign, ShiftLAssign, ShiftRAssign]
instance Arbitrary Expr where
arbitrary = oneof
[ Id <$> arbitrary
, IntLit <$> arbitrary
, UIntLit <$> arbitrary
, FloatLit <$> arbitrary
, StrLit <$> arbitrary
, BoolLit <$> arbitrary
, CharLit <$> arbitrary
, BinOp <$> arbitrary <*> arbitrary <*> arbitrary
, UnOp <$> arbitrary <*> arbitrary
, Call <$> arbitrary <*> arbitrary
, Index <$> arbitrary <*> arbitrary
, Member <$> arbitrary <*> arbitrary
, Cast <$> arbitrary <*> arbitrary
, Sizeof <$> arbitrary
, StructInit <$> arbitrary <*> arbitrary
]
instance Arbitrary Stmt where
arbitrary = oneof
[ Expr <$> arbitrary
, Return <$> arbitrary
, If <$> arbitrary <*> arbitrary <*> arbitrary
, While <$> arbitrary <*> arbitrary
, Assign <$> arbitrary <*> arbitrary <*> arbitrary
, Block <$> arbitrary
, Var <$> arbitrary <*> arbitrary <*> arbitrary
]
-- Massively simplified types: No pointers, void, structs, enums
instance Arbitrary Type where
arbitrary = elements [IntType, UIntType, FloatType, StrType, BoolType,
CharType, PtrType IntType, ArrayType IntType]
instance Arbitrary Bind where
arbitrary = Bind <$> arbitrary <*> arbitrary
instance Arbitrary TLStruct where
arbitrary = Struct <$> arbitrary <*> arbitrary
-- ensure the enum has at least one field
instance Arbitrary TLEnum where
arbitrary = Enum <$> arbitrary <*> listOf1 arbitrary
instance Arbitrary TLFunc where
arbitrary = Func <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance Arbitrary TL where
arbitrary = oneof
[ TLStruct <$> arbitrary
, TLEnum <$> arbitrary
, TLFunc <$> arbitrary
]
instance Arbitrary Program where
arbitrary = Program <$> arbitrary <*> arbitrary <*> arbitrary

8
src/Windows12.hs Normal file
View File

@ -0,0 +1,8 @@
module Windows12 where
import Windows12.Ast
import Windows12.Lexer
import Windows12.Parser
import Windows12.CodeGen
import Windows12.TAst
import Windows12.Semant

216
src/Windows12/Ast.hs Normal file
View File

@ -0,0 +1,216 @@
{-# LANGUAGE OverloadedStrings #-}
module Windows12.Ast where
import Data.Text (Text)
import Data.Text.Prettyprint.Doc
data BinOp
= Add
| Sub
| Mul
| Div
| Mod
| Eq
| Ne
| Lt
| Gt
| Le
| Ge
| And
| Or
| BitAnd
| BitOr
| BitXor
| ShiftL
| ShiftR
deriving (Show, Eq)
data UnOp
= Neg
| Not
| BitNot
| Deref
| AddrOf
deriving (Show, Eq)
data AssignOp
= BaseAssign
| AddAssign
| SubAssign
| MulAssign
| DivAssign
| ModAssign
| BitAndAssign
| BitOrAssign
| BitXorAssign
| ShiftLAssign
| ShiftRAssign
deriving (Show, Eq)
data Expr
= Id Text
| IntLit Int
| UIntLit Word
| FloatLit Double
| StrLit Text
| BoolLit Bool
| CharLit Char
| BinOp BinOp Expr Expr
| UnOp UnOp Expr
| Call Expr [Expr]
| Index Expr Expr
| Member Expr Expr
| Cast Type Expr
| Sizeof Type
| StructInit Text [(Text, Expr)]
deriving (Show, Eq)
data Stmt
= Expr Expr
| Return Expr
| If Expr [Stmt] (Maybe [Stmt])
| While Expr [Stmt]
| Assign AssignOp Expr Expr
| Block [Stmt]
| Var Text (Maybe Type) (Maybe Expr)
deriving (Show, Eq)
data Type
= IntType
| UIntType
| FloatType
| StrType
| BoolType
| CharType
| PtrType Type
| ArrayType Type
| StructType Text
| EnumType Text
| VoidType
deriving (Show, Eq)
data Bind = Bind {bindName :: Text, bindType :: Type}
deriving (Show, Eq)
data TLStruct = Struct {structName :: Text, structFields :: [Bind]}
deriving (Show, Eq)
data TLEnum = Enum {enumName :: Text, enumFields :: [Text]}
deriving (Show, Eq)
data TLFunc = Func {funcName :: Text, funcArgs :: [Bind], funcRetType :: Type, funcBody :: [Stmt]}
deriving (Show, Eq)
data TL = TLStruct TLStruct | TLEnum TLEnum | TLFunc TLFunc
deriving (Show, Eq)
data Program = Program [TLStruct] [TLEnum] [TLFunc]
deriving (Show, Eq)
-- Pretty printing
instance Pretty BinOp where
pretty Add = "+"
pretty Sub = "-"
pretty Mul = "*"
pretty Div = "/"
pretty Mod = "%"
pretty Eq = "=="
pretty Ne = "!="
pretty Lt = "<"
pretty Gt = ">"
pretty Le = "<="
pretty Ge = ">="
pretty And = "&&"
pretty Or = "||"
pretty BitAnd = "&"
pretty BitOr = "|"
pretty BitXor = "^"
pretty ShiftL = "<<"
pretty ShiftR = ">>"
instance Pretty UnOp where
pretty Neg = "-"
pretty Not = "!"
pretty BitNot = "~"
pretty Deref = "*"
pretty AddrOf = "&"
instance Pretty AssignOp where
pretty BaseAssign = "="
pretty AddAssign = "+="
pretty SubAssign = "-="
pretty MulAssign = "*="
pretty DivAssign = "/="
pretty ModAssign = "%="
pretty BitAndAssign = "&="
pretty BitOrAssign = "|="
pretty BitXorAssign = "^="
pretty ShiftLAssign = "<<="
pretty ShiftRAssign = ">>="
instance Pretty Expr where
pretty (Id x) = pretty x
pretty (IntLit x) = pretty x
pretty (UIntLit x) = pretty x
pretty (FloatLit x) = pretty x
pretty (StrLit x) = dquotes (pretty x)
pretty (BoolLit x) = pretty x
pretty (CharLit x) = squotes (pretty x)
pretty (BinOp op l r) = parens (pretty l <+> pretty op <+> pretty r)
pretty (UnOp op e) = pretty op <> parens (pretty e)
pretty (Call f args) = parens (pretty f) <> parens (hsep (punctuate comma (map pretty args)))
pretty (Index arr idx) = parens (pretty arr) <> brackets (pretty idx)
pretty (Member e m) = pretty e <> "." <> pretty m
pretty (Cast t e) = parens (pretty t) <> parens (pretty e)
pretty (Sizeof t) = "sizeof" <> parens (pretty t)
pretty (StructInit s fields) = pretty s <+> lbrace <> line <> indent 4 (vsep (punctuate comma (map (\(n, e) -> pretty n <+> "=" <+> pretty e) fields))) <> line <> rbrace
instance Pretty Stmt where
pretty (Expr e) = pretty e <> semi
pretty (Return e) = "return" <+> pretty e <> semi
pretty (If cond t f) = "if" <+> pretty cond <+> prettyBlock t <+> maybe "" (\f' -> "else" <+> prettyBlock f') f
pretty (While cond body) = "while" <+> pretty cond <+> prettyBlock body
pretty (Assign op l r) = pretty l <+> pretty op <+> pretty r <> semi
pretty (Block stmts) = braces (vsep (map pretty stmts))
pretty (Var n t e) = pretty n <+> maybe "" (\t' -> ":" <+> pretty t') t <+> maybe "" (\e' -> "=" <+> pretty e') e <> semi
instance Pretty Type where
pretty IntType = "int"
pretty UIntType = "uint"
pretty FloatType = "float"
pretty StrType = "str"
pretty BoolType = "bool"
pretty CharType = "char"
pretty (PtrType t) = pretty t <> "*"
pretty (ArrayType t) = pretty t <> "[]"
pretty (StructType s) = pretty s
pretty (EnumType e) = pretty e
pretty VoidType = "void"
instance Pretty Bind where
pretty (Bind n t) = pretty n <+> ":" <+> pretty t
instance Pretty TLStruct where
pretty (Struct n fields) = "struct" <+> pretty n <+> prettyFields fields
instance Pretty TLEnum where
pretty (Enum n fields) = "enum" <+> pretty n <+> prettyFields fields
instance Pretty TLFunc where
pretty (Func n args ret body) =
"fn " <> pretty n <> parens (hsep (punctuate comma (map pretty args))) <+> "->" <+> pretty ret <+> prettyBlock body
instance Pretty TL where
pretty (TLStruct s) = pretty s
pretty (TLEnum e) = pretty e
pretty (TLFunc f) = pretty f
instance Pretty Program where
pretty (Program structs enums funcs) = vsep (map pretty structs) <> line <> vsep (map pretty enums) <> line <> vsep (map pretty funcs)
prettyFields :: (Pretty a) => [a] -> Doc ann
prettyFields fields = lbrace <> line <> indent 4 (vsep (punctuate comma (map pretty fields))) <> line <> rbrace
prettyBlock :: (Pretty a) => [a] -> Doc ann
prettyBlock stmts = lbrace <> line <> indent 4 (vsep (map pretty stmts)) <> line <> rbrace

346
src/Windows12/CodeGen.hs Normal file
View File

@ -0,0 +1,346 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Windows12.CodeGen where
import Windows12.Ast (BinOp(..), UnOp(..), AssignOp(..), Type(..),
Bind(..), TLStruct(..), TLEnum(..))
import Windows12.TAst
import LLVM.AST hiding (ArrayType, VoidType, Call, function)
import LLVM.AST.Type (i32, i1, i8, double, ptr, void)
import qualified LLVM.AST.Constant as C
import LLVM.IRBuilder hiding (double, IRBuilder, ModuleBuilder)
import LLVM.AST.Typed (typeOf)
import LLVM.Prelude (ShortByteString)
import qualified LLVM.AST.IntegerPredicate as IP
import qualified LLVM.AST.FloatingPointPredicate as FP
import Control.Monad.State hiding (void)
import Data.Text (Text, unpack)
import Data.String.Conversions
import Data.String
-- Global program context, used to keep track of operands
data Ctx = Ctx { operands :: [(Text, Operand)],
structs :: [TLStruct],
enums :: [TLEnum],
strings :: [(Text, Operand)] }
deriving (Eq, Show)
type ModuleBuilder = ModuleBuilderT (State Ctx)
type IRBuilder = IRBuilderT ModuleBuilder
-- Allow easy string conversion
instance ConvertibleStrings Text ShortByteString where
convertString = Data.String.fromString . Data.Text.unpack
-- Put an operand into the context with a name
createOperand :: MonadState Ctx m => Text -> Operand -> m ()
createOperand name op = do
ctx <- get
put $ ctx { operands = (name, op) : operands ctx }
-- Take in a source file name, the AST, and return the LLVM IR module
codegen :: Text -> TProgram -> Module
codegen filename (TProgram structs enums funcs) =
flip evalState (Ctx [] [] [] [])
$ buildModuleT (cs filename)
$ do
printf <- externVarArgs (mkName "printf") [ptr i8] i32
createOperand "printf" printf
mapM_ emitTypeDef structs
mapM_ codegenFunc funcs
-- Given a struct name, search the context for the struct and return its fields
getStructFields :: MonadState Ctx m => Text -> m [Bind]
getStructFields name = do
ctx <- get
case filter (\(Struct n _) -> n == name) (structs ctx) of
[] -> error $ "Struct " ++ show name ++ " not found. Valid structs: " ++ show (map (\(Struct n _) -> n) (structs ctx))
[Struct _ fields] -> return fields
_ -> error $ "Multiple structs with name " ++ show name
-- Convert a Windows12 type to an LLVM type
convertType :: MonadState Ctx m => Windows12.Ast.Type -> m LLVM.AST.Type
convertType IntType = return i32
convertType UIntType = return i32
convertType FloatType = return double
convertType StrType = convertType (PtrType CharType)
convertType BoolType = return i1
convertType CharType = return i8
convertType (PtrType t) = ptr <$> convertType t
convertType (ArrayType t) = convertType (PtrType t)
convertType (StructType name) = do
fields <- getStructFields name
types <- mapM (convertType . bindType) fields
return $ StructureType True types -- True indicates packed
convertType (EnumType name) = return i32
convertType VoidType = return void
-- Get the size of a type in bytes
size :: MonadState Ctx m => Windows12.Ast.Type -> m Int
size IntType = return 4
size UIntType = return 4
size FloatType = return 8
size StrType = size (PtrType CharType)
size BoolType = return 1
size CharType = return 1
size (PtrType _) = return 4
size (ArrayType t) = size (PtrType t)
size (StructType name) = do
fields <- getStructFields name
sizes <- mapM (size . bindType) fields
return $ sum sizes
size (EnumType _) = return 8
size VoidType = return 0
-- CodeGen for LValues
codegenLVal :: TLVal -> IRBuilder Operand
codegenLVal (t, (TId name)) = do
ctx <- get
case lookup name (operands ctx) of
Just op -> return op
Nothing -> error $ "Variable " ++ show name ++ " not found"
-- TODO support members of members
codegenLVal ((StructType t), (LTMember ((_, TId sName)) field)) = do
ctx <- get
case lookup sName (operands ctx) of
Just struct -> do
fields <- getStructFields t
offset <- structFieldOffset (Struct sName fields) field
gep struct [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 (fromIntegral offset))]
Nothing -> error $ "Struct " ++ show sName ++ " not found"
codeGenLVal (t, (TDeref e)) = codegenExpr e
codeGenLVal (t, _) = error "Unimplemented or invalid LValue"
-- Given a struct and a field name, return the offset of the field in the struct.
-- In LLVM each field is actually size 1
structFieldOffset :: MonadState Ctx m => TLStruct -> Text -> m Int
structFieldOffset (Struct name fields) field = do
return $ length $ takeWhile (\(Bind n _) -> n /= field) fields
-- CodeGen for expressions
codegenExpr :: TExpr -> IRBuilder Operand
codegenExpr (t, (TVar name)) = flip load 0 =<< codegenLVal (t, (TId name))
codegenExpr (t, (TIntLit i)) = return $ ConstantOperand (C.Int 32 (fromIntegral i))
codegenExpr (t, (TUIntLit i)) = return $ ConstantOperand (C.Int 32 (fromIntegral i))
codegenExpr (t, (TFloatLit f)) = undefined -- TODO floats
codegenExpr (t, (TStrLit s)) = do
strs <- gets strings
case lookup s strs of
-- If the string is already in the context, return it
Just str -> return str
-- Otherwise, create a new global string and add it to the context
Nothing -> do
let str_name = mkName ("str." <> show (length strs))
op <- globalStringPtr (cs s) str_name
modify $ \ctx -> ctx { strings = (s, (ConstantOperand op)) : strs }
return (ConstantOperand op)
codegenExpr (t, (TBoolLit b)) = return $ ConstantOperand (C.Int 1 (if b then 1 else 0))
codegenExpr (t, (TCharLit c)) = return $ ConstantOperand (C.Int 8 (fromIntegral (fromEnum c)))
codegenExpr (t, (TBinOp op lhs rhs)) = do
lhs' <- codegenExpr lhs
rhs' <- codegenExpr rhs
-- TODO pointers, floating points
case op of
Windows12.Ast.Add -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> add lhs' rhs'
_ -> error "Invalid types for add"
Windows12.Ast.Sub -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> sub lhs' rhs'
_ -> error "Invalid types for sub"
Windows12.Ast.Mul -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> mul lhs' rhs'
_ -> error "Invalid types for mul"
Windows12.Ast.Div -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> sdiv lhs' rhs'
_ -> error "Invalid types for div"
Windows12.Ast.Mod -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> srem lhs' rhs'
_ -> error "Invalid types for mod"
Windows12.Ast.Eq -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.EQ lhs' rhs'
_ -> error "Invalid types for eq"
Windows12.Ast.Ne -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.NE lhs' rhs'
_ -> error "Invalid types for ne"
Windows12.Ast.Lt -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.SLT lhs' rhs'
_ -> error "Invalid types for lt"
Windows12.Ast.Gt -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.SGT lhs' rhs'
_ -> error "Invalid types for gt"
Windows12.Ast.Le -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.SLE lhs' rhs'
_ -> error "Invalid types for le"
Windows12.Ast.Ge -> case (typeOf lhs', typeOf rhs') of
(IntegerType 32, IntegerType 32) -> icmp IP.SGE lhs' rhs'
_ -> error "Invalid types for ge"
other -> error $ "Operator " ++ show other ++ " not implemented"
codegenExpr (t, (TUnOp op e)) = undefined -- TODO handle unary operators
-- Function calls: look up the function in operands, then call it with the args
codegenExpr (t, (TCall f args)) = do
ctx <- get
f <- case lookup f (operands ctx) of
Just f -> return f
Nothing -> error $ "Function " ++ show f ++ " not found"
args <- mapM (fmap (, []) . codegenExpr) args
call f args
codegenExpr (t, (TIndex arr idx)) = undefined -- TODO arrays
-- Get the address of the struct field and load it
codegenExpr (t, (TMember ((StructType sName), (TVar sVarName)) m)) = do
ctx <- get
case lookup sVarName (operands ctx) of
Just struct -> do
fields <- getStructFields sName
offset <- structFieldOffset (Struct sVarName fields) m
addr <- gep struct [ConstantOperand (C.Int 32 0), ConstantOperand (C.Int 32 (fromIntegral offset))]
load addr 0
Nothing -> error $ "Struct operand " ++ show sVarName ++ " not found"
codegenExpr (_, (TCast t e)) = undefined -- TODO casts
codegenExpr (_, (TSizeof t)) = ConstantOperand . C.Int 32 . fromIntegral <$> size t
mkTerminator :: IRBuilder () -> IRBuilder ()
mkTerminator instr = do
check <- hasTerminator
unless check instr
-- Codegen for statements
codegenStmt :: TStmt -> IRBuilder ()
-- For expression statements, just evaluate the expression and discard the result
codegenStmt (TExprStmt e) = do
_expr <- codegenExpr e
return ()
codegenStmt (TReturn e) = ret =<< codegenExpr e
-- Generate if statements, with a merge block at the end
codegenStmt (TIf cond t f) = mdo
cond' <- codegenExpr cond
condBr cond' then' else'
then' <- block `named` "then"
codegenStmt (TBlock t)
mkTerminator $ br merge
else' <- block `named` "else"
codegenStmt (case f of
Just f' -> TBlock f'
Nothing -> TBlock [])
mkTerminator $ br merge
merge <- block `named` "merge"
return ()
-- Generate while loops, with a merge block at the end
codegenStmt (TWhile cond body) = mdo
br condBlock
condBlock <- block `named` "cond"
cond' <- codegenExpr cond
condBr cond' loop end
loop <- block `named` "loop"
codegenStmt (TBlock body)
mkTerminator $ br condBlock
end <- block `named` "end"
return ()
codegenStmt (TAssign BaseAssign l e) = do
op <- codegenExpr e
var <- codegenLVal l
store var 0 op
codegenStmt (TAssign AddAssign l e) = do
op <- codegenExpr e
var <- codegenLVal l
val <- load var 0
store var 0 =<< add val op
codegenStmt (TAssign SubAssign l e) = do
op <- codegenExpr e
var <- codegenLVal l
val <- load var 0
store var 0 =<< sub val op
-- A block is just a list of statements
codegenStmt (TBlock stmts) = mapM_ codegenStmt stmts
-- Since the vars are already allocated by genBody, we just need to assign the value
codegenStmt (TDeclVar name t (Just e)) = codegenStmt (TAssign BaseAssign (t, (TId name)) e)
-- Do nothing with variable declaration if no expression is given
-- This is because allocation is done already
codegenStmt (TDeclVar name _ Nothing) = return ()
codegenStmt s = error $ "Unimplemented or invalid statement " ++ show s
-- Generate code for a function
-- First create the function, then allocate space for the arguments and locals
codegenFunc :: TTLFunc -> ModuleBuilder ()
codegenFunc func@(TTLFunc name args retType body) = mdo
createOperand name f
(f, strs) <- do
params' <- mapM mkParam args
retType' <- convertType retType
f <- function (mkName (cs name)) params' retType' genBody
strs <- gets strings
return (f, strs)
modify $ \ctx -> ctx { strings = strs }
where
mkParam (Bind name t) = (,) <$> convertType t <*> pure (ParameterName (cs name))
genBody :: [Operand] -> IRBuilder ()
genBody ops = do
forM_ (zip ops args) $ \(op, (Bind name t)) -> do
addr <- alloca (typeOf op) Nothing 0
store addr 0 op
createOperand name addr
forM_ (getLocals func) $ \(Bind name t) -> do
ltype <- convertType t
addr <- alloca ltype Nothing 0
createOperand name addr
codegenStmt (TBlock body)
-- Given a function, get all the local variables
-- Used so allocation can be done before the function body
getLocals :: TTLFunc -> [Bind]
getLocals (TTLFunc _ args _ body) = blockGetLocals body
blockGetLocals :: [TStmt] -> [Bind]
blockGetLocals = concatMap stmtGetLocals
stmtGetLocals :: TStmt -> [Bind]
stmtGetLocals (TDeclVar n t _) = [Bind n t]
stmtGetLocals (TBlock stmts) = blockGetLocals stmts
stmtGetLocals (TIf _ t f) = blockGetLocals t ++ maybe [] blockGetLocals f
stmtGetLocals (TWhile _ body) = blockGetLocals body
stmtGetLocals _ = []
-- Create structs
emitTypeDef :: TLStruct -> ModuleBuilder LLVM.AST.Type
emitTypeDef (Struct name fields) = do
modify $ \ctx -> ctx { structs = Struct name fields : structs ctx }
sType <- convertType (StructType name)
typedef (mkName (cs ("struct." <> name))) (Just sType)

82
src/Windows12/Lexer.hs Normal file
View File

@ -0,0 +1,82 @@
{-# LANGUAGE OverloadedStrings #-}
module Windows12.Lexer where
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
type Parser = Parsec Void Text
sc :: Parser ()
sc =
L.space
space1
(L.skipLineComment "#")
(L.skipBlockComment "/*" "*/")
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
symbol :: Text -> Parser Text
symbol = L.symbol sc
charLiteral :: Parser Char
charLiteral = between (char '\'') (char '\'') L.charLiteral
stringLiteral :: Parser Text
stringLiteral = T.pack <$> (char '"' *> manyTill L.charLiteral (char '"'))
intLiteral :: Parser Int
intLiteral = lexeme L.decimal
uintLiteral :: Parser Word
uintLiteral = lexeme L.decimal <* char 'u'
floatLiteral :: Parser Double
floatLiteral = lexeme L.float
reserved :: Text -> Parser ()
reserved word = lexeme (string word *> notFollowedBy alphaNumChar)
reservedWords :: [Text]
reservedWords =
[ "if",
"else",
"while",
"for",
"return",
"int",
"uint",
"float",
"char",
"bool",
"struct",
"sizeof",
"true",
"false",
"fn",
"on",
"var"
]
identifier :: Parser Text
identifier = (lexeme . try) (p >>= check)
where
p = fmap T.pack $ (:) <$> letterChar <*> many (alphaNumChar <|> char '_')
check x =
if x `elem` reservedWords
then fail $ "keyword " <> show x <> " cannot be an identifier"
else return x
parens :: Parser a -> Parser a
parens = between (symbol "(") (symbol ")")
braces :: Parser a -> Parser a
braces = between (symbol "{") (symbol "}")
brackets :: Parser a -> Parser a
brackets = between (symbol "[") (symbol "]")

163
src/Windows12/Parser.hs Normal file
View File

@ -0,0 +1,163 @@
{-# LANGUAGE OverloadedStrings #-}
module Windows12.Parser (programP) where
import Control.Monad.Combinators.Expr
import Text.Megaparsec
import Windows12.Ast
import Windows12.Lexer
opTable :: [[Operator Parser Expr]]
opTable =
[ [ InfixL $ Member <$ symbol ".",
InfixL $ (\l r -> Member (UnOp Deref l) r) <$ symbol "->"
],
[ unary (UnOp Neg) "-",
unary (UnOp Not) "!",
unary (UnOp BitNot) "~",
unary (UnOp Deref) "*",
unary (UnOp AddrOf) "&"
],
[ Postfix $ flip Index <$> (symbol "[" *> exprP <* symbol "]")
],
[ Postfix $ flip Call <$> parens (sepBy exprP (symbol ","))
],
[ infixL' Mul "*",
infixL' Div "/",
infixL' Mod "%"
],
[ infixL' Add "+",
infixL' Sub "-"
],
[ infixL' ShiftL "<<",
infixL' ShiftR ">>"
],
[ infixL Le "<=",
infixL Lt "<",
infixL Ge ">=",
infixL Gt ">"
],
[ infixL Eq "==",
infixL Ne "!="
],
[ infixL' BitAnd "&"
],
[ infixL' BitXor "^"
],
[ infixL' BitOr "|"
],
[ infixL And "&&"
],
[ infixL Or "||"
]
]
where
unary op sym = Prefix $ foldr1 (.) <$> some (op <$ symbol sym)
infixL op sym = InfixL $ BinOp op <$ symbol sym
infixL' op sym = InfixL $ BinOp op <$ operator sym
infixR op sym = InfixR $ BinOp op <$ symbol sym
operator sym = lexeme $ try $ (symbol sym <* notFollowedBy opChar)
opChar = oneOf ("+-*/%<>&|^=!~" :: [Char])
termP :: Parser Expr
termP =
parens exprP
<|> IntLit <$> intLiteral
<|> UIntLit <$> uintLiteral
<|> try (FloatLit <$> floatLiteral)
<|> StrLit <$> stringLiteral
<|> BoolLit <$> (reserved "true" *> pure True <|> reserved "false" *> pure False)
<|> CharLit <$> charLiteral
<|> try (Sizeof <$> (reserved "sizeof" *> typeP))
<|> try (Cast <$> (parens typeP) <*> termP)
<|> try (StructInit <$> identifier <*> braces (sepEndBy1 ((,) <$> identifier <* symbol ":" <*> exprP) (symbol ",")))
<|> Id <$> identifier
exprP :: Parser Expr
exprP = makeExprParser termP opTable
structP :: Parser TLStruct
structP = do
reserved "struct"
name <- identifier
fields <- braces (sepEndBy (Bind <$> identifier <* symbol ":" <*> typeP) (symbol ","))
return $ Struct name fields
typeP :: Parser Type
typeP = do
t <-
ArrayType <$> (brackets typeP)
<|> IntType <$ reserved "int"
<|> UIntType <$ reserved "uint"
<|> FloatType <$ reserved "float"
<|> StrType <$ reserved "str"
<|> BoolType <$ reserved "bool"
<|> CharType <$ reserved "char"
<|> StructType <$> identifier
foldr (const PtrType) t <$> many (symbol "*")
assignP :: Parser Stmt
assignP = do
lhs <- exprP
op <-
AddAssign <$ symbol "+="
<|> SubAssign <$ symbol "-="
<|> MulAssign <$ symbol "*="
<|> DivAssign <$ symbol "/="
<|> ModAssign <$ symbol "%="
<|> BitAndAssign <$ symbol "&="
<|> BitOrAssign <$ symbol "|="
<|> BitXorAssign <$ symbol "^="
<|> ShiftLAssign <$ symbol "<<="
<|> ShiftRAssign <$ symbol ">>="
<|> BaseAssign <$ symbol "="
Assign op lhs <$> exprP <* symbol ";"
stmtP :: Parser Stmt
stmtP =
Return <$> (reserved "return" *> exprP <* symbol ";")
<|> (If <$> (reserved "if" *> exprP) <*> braces (many stmtP) <*> optional (reserved "else" *> braces (many stmtP)))
<|> (While <$> (reserved "while" *> exprP) <*> braces (many stmtP))
<|> (Var <$> (reserved "var" *> identifier) <*> optional (symbol ":" *> typeP) <*> optional (symbol "=" *> exprP) <* symbol ";")
<|> try assignP
<|> Expr <$> exprP <* symbol ";"
<|> Block <$> braces (many stmtP)
funcP :: Parser TLFunc
funcP = do
reserved "fn"
name <- identifier
args <- parens (sepBy (Bind <$> identifier <* symbol ":" <*> typeP) (symbol ","))
retType <- (symbol "->" *> typeP) <|> pure VoidType
body <- braces (many stmtP)
return $ Func name args retType body
enumP :: Parser TLEnum
enumP = do
reserved "enum"
name <- identifier
fields <- braces (sepEndBy1 identifier (symbol ","))
return $ Enum name fields
memberFuncP :: Parser TLFunc
memberFuncP = do
reserved "fn"
reserved "on"
self <- typeP
name <- identifier
args <- parens (sepBy (Bind <$> identifier <* symbol ":" <*> typeP) (symbol ","))
retType <- (symbol "->" *> typeP) <|> pure VoidType
body <- braces (many stmtP)
return $ Func name (Bind "self" self : args) retType body
organize :: [TL] -> Program
organize tls = Program structs enums funcs
where
structs = [s | TLStruct s <- tls]
enums = [e | TLEnum e <- tls]
funcs = [f | TLFunc f <- tls]
programP :: Parser Program
programP = between sc eof $ do
tls <- many (TLStruct <$> structP <|> TLEnum <$> enumP <|> try (TLFunc <$> memberFuncP) <|> TLFunc <$> funcP)
return $ organize tls

263
src/Windows12/Semant.hs Normal file
View File

@ -0,0 +1,263 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Windows12.Semant where
import Data.Text (Text)
import Control.Monad.State
import Data.List (find)
import Windows12.Ast as Ast
import Windows12.TAst as TAst
suppliedFuncs :: [Text]
suppliedFuncs = ["printf"]
-- Convert an Ast to a TAst
-- Performs type inference and type checking
data Ctx = Ctx { structs :: [TLStruct],
enums :: [TLEnum],
funcs :: [TTLFunc],
vars :: [(Text, Type)] }
deriving (Eq, Show)
-- Main conversion function. May return an error message if the program
-- is not well-typed.
convert :: Ast.Program -> Either String TAst.TProgram
convert (Ast.Program structs enums funcs) = do
let ctx = Ctx structs enums [] []
let (funcs', _) = runState (mapM convertFunc funcs) ctx
return $ TAst.TProgram structs enums funcs'
-- Convert a TLFunc (Top Level Function) to a TTLFunc (Typed Top Level Function)
-- Note that the function must be added to the context before converting statements
-- of the function. This is because the function may call itself recursively.
-- After converting the function, the function's statements are converted
-- and added to the context.
convertFunc :: MonadState Ctx m => Ast.TLFunc -> m TAst.TTLFunc
convertFunc (Ast.Func name args retType body) = do
args' <- mapM (\(Bind name t) -> return (name, t)) args
oldFuncs <- gets funcs
modify (\ctx -> ctx { funcs = funcs ctx ++ [TTLFunc name args retType []], vars = args' })
body' <- mapM convertStmt body
ctx <- get
let func = (last $ funcs ctx) { TAst.funcBody = body' }
put $ ctx { funcs = oldFuncs ++ [func] }
return func
-- Convert a statement
convertStmt :: MonadState Ctx m => Ast.Stmt -> m TAst.TStmt
convertStmt (Ast.Expr expr) = do
expr' <- convertExpr expr
return $ TAst.TExprStmt expr'
convertStmt (Ast.Return expr) = do
expr' <- convertExpr expr
return $ TAst.TReturn expr'
convertStmt (Ast.If cond thenStmts elseStmts) = do
thenStmts' <- mapM convertStmt thenStmts
elseStmts' <- mapM convertStmt $ maybe [] id elseStmts
cond' <- convertExpr cond
return $ TAst.TIf cond' thenStmts' (Just elseStmts')
convertStmt (Ast.While cond stmts) = do
stmts' <- mapM convertStmt stmts
cond' <- convertExpr cond
return $ TAst.TWhile cond' stmts'
convertStmt (Ast.Assign op lval expr) = do
lval' <- convertLVal lval
expr' <- convertExpr expr
return $ TAst.TAssign op lval' expr'
convertStmt (Ast.Block stmts) = do
stmts' <- mapM convertStmt stmts
return $ TAst.TBlock stmts'
convertStmt (Ast.Var name (Just t) maybeExpr) = do
expr' <- maybe (return Nothing) (fmap Just . convertExpr) maybeExpr
modify (\ctx -> ctx { vars = (name, t) : vars ctx })
return $ TAst.TDeclVar name t expr'
-- TODO
convertStmt (Ast.Var name Nothing maybeExpr) = error "Type inference not implemented"
-- Convert an expression to an LValue
-- Only certain expressions are allowed as LValues
convertLVal :: MonadState Ctx m => Ast.Expr -> m TAst.TLVal
convertLVal (Ast.Id name) = do
ctx <- get
case lookup name (vars ctx) of
Just t -> return (t, TAst.TId name)
Nothing -> error $ "Variable " ++ show name ++ " not in scope"
convertLVal (Ast.Index arr idx) = do
arr' <- convertLVal arr
idx' <- convertExpr idx
return (fst arr', TAst.LTIndex arr' idx')
convertLVal (Ast.Member e (Id m)) = do
e' <- convertLVal e
return (fst e', TAst.LTMember e' m)
convertLVal (Ast.Member e m) = do error $ "Invalid member access " ++ show m ++ " on " ++ show e
convertLVal (Ast.UnOp Ast.Deref e) = error "Dereferencing not implemented"
convertLVal e = do error $ "Invalid or unimplemented LValue " ++ show e
-- Convert an expression
convertExpr :: MonadState Ctx m => Ast.Expr -> m TAst.TExpr
convertExpr (Ast.Id name) = do
ctx <- get
case lookup name (vars ctx) of
Just t -> return (t, TAst.TVar name)
Nothing -> error $ "Variable " ++ show name ++ " not in scope"
convertExpr (Ast.IntLit x) = return (IntType, TAst.TIntLit x)
convertExpr (Ast.UIntLit x) = return (UIntType, TAst.TUIntLit x)
convertExpr (Ast.FloatLit x) = return (FloatType, TAst.TFloatLit x)
convertExpr (Ast.StrLit x) = return (StrType, TAst.TStrLit x)
convertExpr (Ast.BoolLit x) = return (BoolType, TAst.TBoolLit x)
convertExpr (Ast.CharLit x) = return (CharType, TAst.TCharLit x)
convertExpr (Ast.BinOp Add l r) = arithOp Add l r
convertExpr (Ast.BinOp Sub l r) = arithOp Sub l r
convertExpr (Ast.BinOp Mul l r) = arithOp Mul l r
convertExpr (Ast.BinOp Div l r) = arithOp Div l r
convertExpr (Ast.BinOp Mod l r) = arithOp Mod l r
convertExpr (Ast.BinOp Eq l r) = compOp Eq l r
convertExpr (Ast.BinOp Ne l r) = compOp Ne l r
convertExpr (Ast.BinOp Lt l r) = compOp Lt l r
convertExpr (Ast.BinOp Gt l r) = compOp Gt l r
convertExpr (Ast.BinOp Le l r) = compOp Le l r
convertExpr (Ast.BinOp Ge l r) = compOp Ge l r
convertExpr (Ast.BinOp And l r) = boolOp And l r
convertExpr (Ast.BinOp Or l r) = boolOp Or l r
convertExpr (Ast.BinOp BitAnd l r) = bitOp BitAnd l r
convertExpr (Ast.BinOp BitOr l r) = bitOp BitOr l r
convertExpr (Ast.BinOp BitXor l r) = bitOp BitXor l r
convertExpr (Ast.BinOp ShiftL l r) = shiftOp ShiftL l r
convertExpr (Ast.BinOp ShiftR l r) = shiftOp ShiftR l r
convertExpr (Ast.UnOp Neg e) = do
e' <- convertExpr e
if fst e' `elem` [IntType, UIntType, FloatType]
then return (fst e', TAst.TUnOp Neg e')
else error $ "Type mismatch: " ++ show e
convertExpr (Ast.UnOp Not e) = do
e' <- convertExpr e
if fst e' == BoolType
then return (BoolType, TAst.TUnOp Not e')
else error $ "Type mismatch: " ++ show e
convertExpr (Ast.UnOp BitNot e) = undefined
convertExpr (Ast.UnOp Deref e) = undefined
convertExpr (Ast.UnOp AddrOf e) = undefined
-- TODO type check function return
-- TODO ensure returns on all paths
-- Lower priority since LLVM checks this also
convertExpr (Ast.Call (Id f) args) = do
ctx <- get
if f == "printf"
then do
args' <- mapM convertExpr args
return (IntType, TAst.TCall "printf" args')
else case find (\(TTLFunc n a r _) -> n == f) (funcs ctx) of
Just t -> do
args' <- mapM convertExpr args
if length args' == length (TAst.funcArgs t) && all (\(t1, t2) -> t1 == t2) (zip (map fst args') (map bindType (TAst.funcArgs t)))
then return (TAst.funcRetType t, TAst.TCall f args')
else error $ "Type mismatch in call to " ++ show f
Nothing -> error $ "Function " ++ show f ++ " not in scope. Available functions: " ++ show (map TAst.funcName (funcs ctx))
convertExpr (Ast.Index arr idx) = do
arr' <- convertExpr arr
idx' <- convertExpr idx
case fst arr' of
ArrayType t -> if fst idx' == IntType
then return (t, TAst.TIndex arr' idx')
else error $ "Index must be an integer: " ++ show idx
_ -> error $ "Indexing non-array: " ++ show arr
convertExpr (Ast.Cast t e) = do
e' <- convertExpr e
return (t, TAst.TCast t e')
convertExpr (Ast.Sizeof t) = return (IntType, TAst.TSizeof t)
convertExpr (Ast.Member e (Id m)) = do
e' <- convertExpr e
case fst e' of
StructType name -> do
ctx <- get
case find (\(Struct n _) -> n == name) (structs ctx) of
Just (Struct _ binds) -> case find (\(Bind n t) -> n == m) binds of
Just (Bind _ t) -> return (t, TAst.TMember e' m)
Nothing -> error $ "Field " ++ show m ++ " not in struct " ++ show name
Nothing -> error $ "Struct " ++ show name ++ " not in scope"
_ -> error $ "Member access on non-struct " ++ show e
convertExpr (Ast.StructInit name fields) = do
ctx <- get
case find (\(Struct n _) -> n == name) (structs ctx) of
Just (Struct _ binds) -> do
fields' <- mapM (\(n, e) -> do
e' <- convertExpr e
case find (\(Bind n' t) -> n == n') binds of
Just (Bind _ t) -> if fst e' == t
then return (n, e')
else error $ "Type mismatch in struct initialization: " ++ show e
Nothing -> error $ "Field " ++ show n ++ " not in struct " ++ show name) fields
return (StructType name, TAst.TStructInit name fields')
Nothing -> error $ "Struct " ++ show name ++ " not in scope"
convertExpr e = error $ "Invalid or Unimplemented conversion for expression " ++ show e
-- Ensure that the types of the left and right expressions are the same
-- and return the type of the result
arithOp :: MonadState Ctx m => Ast.BinOp -> Ast.Expr -> Ast.Expr -> m TAst.TExpr
arithOp o l r = do
l' <- convertExpr l
r' <- convertExpr r
if fst l' == fst r'
then return (fst l', TAst.TBinOp o l' r')
else error $ "Type mismatch: " ++ show l ++ " and " ++ show r
-- Ensure that the types of the left and right expressions are the same
-- and return a boolean type
compOp :: MonadState Ctx m => Ast.BinOp -> Ast.Expr -> Ast.Expr -> m TAst.TExpr
compOp o l r = do
l' <- convertExpr l
r' <- convertExpr r
if fst l' == fst r'
then return (BoolType, TAst.TBinOp o l' r')
else error $ "Type mismatch: " ++ show l ++ " and " ++ show r
-- Ensure that the types of both expressions are boolean
-- and return a boolean type
boolOp :: MonadState Ctx m => Ast.BinOp -> Ast.Expr -> Ast.Expr -> m TAst.TExpr
boolOp o l r = do
l' <- convertExpr l
r' <- convertExpr r
if fst l' == fst r' && fst l' == BoolType
then return (BoolType, TAst.TBinOp o l' r')
else error $ "Type mismatch: " ++ show l ++ " and " ++ show r
bitOp :: MonadState Ctx m => Ast.BinOp -> Ast.Expr -> Ast.Expr -> m TAst.TExpr
bitOp o l r = do error $ "Bit operations not implemented"
shiftOp :: MonadState Ctx m => Ast.BinOp -> Ast.Expr -> Ast.Expr -> m TAst.TExpr
shiftOp o l r = do error $ "Shift operations not implemented"

54
src/Windows12/TAst.hs Normal file
View File

@ -0,0 +1,54 @@
module Windows12.TAst where
import Data.Text (Text)
import Windows12.Ast as Ast
-- "Typed AST". A second AST that contains more type information
-- Makes verification easier, and is needed to determine type
-- of structs when accessing members in CodeGen
type TExpr = (Type, TExpr')
data TExpr'
= TVar Text
| TIntLit Int
| TUIntLit Word
| TFloatLit Double
| TStrLit Text
| TBoolLit Bool
| TCharLit Char
| TBinOp BinOp TExpr TExpr
| TUnOp UnOp TExpr
| TCall Text [TExpr]
| TIndex TExpr TExpr
| TMember TExpr Text
| TCast Type TExpr
| TSizeof Type
| TStructInit Text [(Text, TExpr)]
deriving (Show, Eq)
type TLVal = (Type, TLVal')
data TLVal'
= TDeref TExpr
| TId Text
| LTIndex TLVal TExpr
| LTMember TLVal Text
deriving (Show, Eq)
data TStmt
= TExprStmt TExpr
| TReturn TExpr
| TIf TExpr [TStmt] (Maybe [TStmt])
| TWhile TExpr [TStmt]
| TAssign AssignOp TLVal TExpr
| TBlock [TStmt]
| TDeclVar Text Type (Maybe TExpr)
deriving (Show, Eq)
data TTLFunc = TTLFunc {funcName :: Text, funcArgs :: [Bind], funcRetType :: Type, funcBody :: [TStmt]}
deriving (Show, Eq)
data TProgram = TProgram [TLStruct] [TLEnum] [TTLFunc]
deriving (Show, Eq)

43
test/arith.w12 Normal file
View File

@ -0,0 +1,43 @@
fn factorial(n: int) -> int {
var result: int;
if n == 0 {
result = 1;
} else {
result = n * factorial(n - 1);
}
return result;
}
fn is_positive(n: int) -> bool {
return n > 0;
}
fn main() -> int {
var a: int = 5;
printf("a = %d\n", a);
var b: int = 3;
printf("b = %d\n", b);
var c: int = (a + b) * 2 - 1;
c += factorial(5);
printf("c = %d\n", c);
while c > 0 {
printf("c = %d\n", c);
if c > 50 {
printf("c is greater than 50\n");
} else {
printf("c is less than or equal to 50\n");
}
c -= 16;
}
printf("End result: %d\n", c);
return 0;
}

1
test/bad.w12 Normal file
View File

@ -0,0 +1 @@
function test() {}

21
test/basic-structs.w12 Normal file
View File

@ -0,0 +1,21 @@
struct Test {
a: int,
b: char,
}
fn main() -> int {
var t: Test;
t.a = 5;
t.b = 'a';
printf("t.a = %d\n", t.a);
printf("t.b = %c\n", t.b);
t.a += 3;
t.b = 'b';
printf("t.a = %d\n", t.a);
printf("t.b = %c\n", t.b);
return 0;
}

0
test/empty.w12 Normal file
View File

38
test/fib.w12 Normal file
View File

@ -0,0 +1,38 @@
fn rec_fib(n: int) -> int {
if n <= 1 {
return n;
}
return rec_fib(n - 1) + rec_fib(n - 2);
}
fn loop_fib(n: int) -> int {
var a: int = 0;
var b: int = 1;
var i: int = 0;
while i < n {
var c: int = a + b;
a = b;
b = c;
i += 1;
}
return a;
}
fn main() -> int {
var n: int = 20;
var rec_result: int = rec_fib(n);
var loop_result: int = loop_fib(n);
printf("Fibonacci of %d via recursion is %d\n", n, rec_result);
printf("Fibonacci of %d via loop is %d\n", n, loop_result);
if rec_result == loop_result {
printf("Results match\n");
} else {
printf("Results do not match\n");
}
return 0;
}

32
test/functions.w12 Normal file
View File

@ -0,0 +1,32 @@
fn mult3(a: int, b: int, c: int) -> int {
return a * b * c;
}
fn loop_test() -> int {
var i: int = 0;
var result: int = 0;
while i < 10 {
printf("i = %d\n", i);
result += i;
i += 1;
}
return result;
}
fn main() -> int {
var a: int = 5;
var b: int = 3;
var c: int = 2;
var result: int = mult3(a, b, c);
printf("Result: %d\n", result);
result = loop_test();
printf("Result: %d\n", result);
return 0;
}

64
test/hello.w12 Normal file
View File

@ -0,0 +1,64 @@
# Create an enum
enum AnimalType {
Dog,
Cat,
}
# Create a struct
struct Pet {
name: [char], # A list of characters
age: uint, # An unsigned 32-bit integer
type: AnimalType,
living: bool,
}
# Create a function that can be called on a Pet
fn on Pet rename(newName: [char]) {
self.name = newName;
}
# Create another struct
struct Person {
pet: Pet,
name: [char],
age: uint,
living: bool,
}
fn on Person growUp() {
self.age += 1;
}
fn main() -> int {
# Create an instance of Pet
# "let" creates an immutable binding
var dog = Pet {
name: "Fido",
age: 3,
type: AnimalType.Dog,
};
# Create a (variable) instance of Person
# "var" creates a mutable binding
var person = Person {
pet: dog,
name: "Fred",
age: 41,
};
# Create a new name for the Pet
var new_name = "George";
person.pet.rename(new_name);
# Print out the person's name and age
# Uses C for I/O
printf("Person %s is %u years old.\n", person.name, person.age);
if person.age % 2 == 0 {
printf("Age is even\n");
} else {
printf("Age is odd\n");
}
*test[12](3);
return 0;
}

View File

@ -63,7 +63,14 @@ executable windows12
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
other-modules:
Windows12
Windows12.Ast
Windows12.Lexer
Windows12.Parser
Windows12.CodeGen
Windows12.TAst
Windows12.Semant
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
@ -72,7 +79,46 @@ executable windows12
build-depends:
base >= 4.15.1 && < 4.16,
llvm-hs-pure >= 9.0.0 && < 9.1,
llvm-hs-pretty >= 0.9.0 && < 0.10,
megaparsec >= 9.6.1 && < 9.7,
text >= 1.2.5 && < 1.3,
parser-combinators >= 1.3.0 && < 1.4,
prettyprinter >= 1.5.1 && < 1.6,
string-conversions >= 0.4.0 && < 0.5,
mtl >= 2.2.2 && < 2.3,
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010
executable windows12-qc
-- Import common warning flags.
import: warnings
-- .hs or .lhs file containing the Main module.
main-is: QuickCheckTests.hs
-- Modules included in this executable, other than Main.
other-modules:
Windows12.Ast
Windows12.Lexer
Windows12.Parser
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends:
base >= 4.15.1 && < 4.16,
megaparsec >= 9.6.1 && < 9.7,
text >= 1.2.5 && < 1.3,
parser-combinators >= 1.3.0 && < 1.4,
prettyprinter >= 1.5.1 && < 1.6,
string-conversions >= 0.4.0 && < 0.5,
mtl >= 2.2.2 && < 2.3,
QuickCheck >= 2.14.2 && < 2.15,
-- Directories containing source files.
hs-source-dirs: src