Skip to content

Commit

Permalink
Merge pull request #15 from EpitechPromo2027/jabolol/program-scaffold
Browse files Browse the repository at this point in the history
feat: support multiple `top` level expressions
  • Loading branch information
Jabolol authored Dec 3, 2024
2 parents e477b7a + 7a9f5c4 commit 03f60d0
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
20 changes: 16 additions & 4 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,22 @@ main :: IO ()
main = do
let ast =
T.AST
[ T.If
(T.Lit (T.LInt 1))
(T.Op T.Add (T.Lit (T.LInt 2)) (T.Lit (T.LInt 3)))
(T.Op T.Mult (T.Lit (T.LInt 4)) (T.Lit (T.LInt 5)))
[ T.Define
"$$generated"
( T.If
(T.Lit (T.LInt 1))
( T.Call
( T.Lambda ["x"] (T.Op T.Add (T.Var "x") (T.Lit (T.LInt 1)))
)
[T.Lit (T.LInt 2)]
)
( T.Call
( T.Lambda ["y"] (T.Op T.Sub (T.Var "y") (T.Lit (T.LInt 1)))
)
[T.Lit (T.LInt 2)]
)
)
]

let output = TL.unpack $ P.ppllvm $ C.codegen ast
writeFile "demo/generated.ll" output
12 changes: 10 additions & 2 deletions demo/add.lisp
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
; Sample LISP code, evaluates to `5`
(if 1 (+ 2 3) (* 4 5))
; Sample LISP code, evaluates to `3`
(define lambda_0 (lambda (x) (+ x 1)))
(define lambda_1 (lambda (y) (- y 1)))

(define $$generated
(if (= 1 1)
(lambda_0 2)
(lambda_1 2)))

($$generated)
8 changes: 5 additions & 3 deletions glados.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,18 @@ common warnings
library
import: warnings
exposed-modules:
Ast.Types
Ast.Tokenizer
Ast.Types
Codegen.Codegen
Misc

build-depends:
base ^>=4.17.2.1,
bytestring >=0.11.2 && <0.12,
containers >=0.6.7 && <0.7,
llvm-hs-pretty >=0.9.0 && <0.10,
llvm-hs-pure >=9.0.0 && <9.1,
megaparsec >= 9.7.0
megaparsec >=9.7.0,

hs-source-dirs: lib
default-language: Haskell2010
Expand All @@ -54,8 +55,9 @@ test-suite glados-test
default-language: Haskell2010
type: exitcode-stdio-1.0
other-modules:
Misc.MiscSpec,
Ast.TokenizerSpec
Misc.MiscSpec

hs-source-dirs: test
main-is: Spec.hs
build-depends:
Expand Down
50 changes: 44 additions & 6 deletions lib/Codegen/Codegen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
module Codegen.Codegen where

import qualified Ast.Types as AT
import qualified Control.Monad as CM
import qualified Control.Monad.Fix as Fix
import qualified Data.ByteString.Short as BS
import qualified LLVM.AST as AST
import qualified LLVM.AST.Constant as C
import qualified LLVM.AST.IntegerPredicate as IP
Expand All @@ -16,16 +18,29 @@ import qualified LLVM.IRBuilder.Module as M
import qualified LLVM.IRBuilder.Monad as IRM

-- | Type alias for the monad stack used for code generation.
type MonadCodegen m = (IRM.MonadIRBuilder m, Fix.MonadFix m)
type MonadCodegen m = (IRM.MonadIRBuilder m, M.MonadModuleBuilder m, Fix.MonadFix m)

-- | Converts a parameter name to a pair of type and parameter name.
-- The `toParamType` function takes a string and returns a pair of type and parameter name.
-- The type is always `i32` (32-bit integer), and the parameter name is the input string.
toParamType :: String -> (T.Type, M.ParameterName)
toParamType param =
( T.i32,
M.ParameterName $ BS.pack $ map (fromIntegral . fromEnum) param
)

-- | Generates LLVM code for a given abstract syntax tree (AST).
-- The `codegen` function takes an AST and returns the corresponding LLVM module.
codegen :: AT.AST -> AST.Module
codegen ast = M.buildModule "generated" $ do
M.function "$$generated" [] T.i32 $ \_ -> do
result <- case ast of
AT.AST exprs -> generateExpr $ head exprs
I.ret result
codegen (AT.AST exprs) = M.buildModule "$$generated" $ do
IRM.runIRBuilderT IRM.emptyIRBuilder $ mapM_ generateTopLevel exprs

-- | Generates LLVM code for a top-level expression.
-- The `generateTopLevel` function takes an expression and generates the corresponding LLVM code.
generateTopLevel :: (MonadCodegen m) => AT.Expr -> m ()
generateTopLevel expr = case expr of
AT.Define name body -> CM.void $ buildLambda (AST.mkName name) [] body
_ -> error ("Unsupported top-level expression: " ++ show expr)

-- | Maps binary operators to LLVM instructions.
binaryOps :: [(AT.Operation, AST.Operand -> AST.Operand -> (IRM.MonadIRBuilder m) => m AST.Operand)]
Expand Down Expand Up @@ -70,6 +85,19 @@ generateOp op e1 e2 = do
Just instruction -> instruction v1 v2
Nothing -> error $ "Unsupported operator: " ++ show op

-- | Generates LLVM code for a lambda expression.
-- The `buildLambda` function takes a name, a list of parameter names, and a body expression,
-- and returns an LLVM operand representing the lambda function.
buildLambda :: (MonadCodegen m) => AST.Name -> [String] -> AT.Expr -> m AST.Operand
buildLambda name params body = do
M.function
name
[toParamType param | param <- params]
T.i32
$ \_ -> do
result <- generateExpr body
I.ret result

-- | Generates an LLVM operand for an expression.
-- The `generateExpr` function recursively processes different expression types
-- and generates the corresponding LLVM code.
Expand All @@ -81,4 +109,14 @@ generateExpr expr = case expr of
pure $ AST.ConstantOperand $ C.Int 1 (if b then 1 else 0)
AT.Op op e1 e2 -> generateOp op e1 e2
AT.If cond then_ else_ -> generateIf cond then_ else_
AT.Call func args -> do
func' <- case func of
AT.Lambda params body -> do
uniqueName <- IRM.freshName "lambda"
buildLambda uniqueName params body
AT.Var name -> pure $ AST.LocalReference T.i32 $ AST.mkName name
_ -> generateExpr func
args' <- mapM generateExpr args
I.call func' [(arg, []) | arg <- args']
AT.Var name -> pure $ AST.LocalReference T.i32 $ AST.mkName name
_ -> error ("Unimplemented expression type: " ++ show expr)

0 comments on commit 03f60d0

Please # to comment.