Compare commits

...
This repository has been archived on 2024-04-05. You can view files and clone it, but cannot push or open issues/pull-requests.

3 Commits

Author SHA1 Message Date
Ethan Reece cca9262fc3
Allow parsing signed integers 2023-09-30 02:54:48 -05:00
Ethan Reece 83aa5bd4d3
Update readme and examples 2023-09-30 02:00:51 -05:00
Ethan Reece 067150a682
Parse expr tokens to list 2023-09-30 01:52:31 -05:00
12 changed files with 137 additions and 69 deletions

View File

@ -12,9 +12,17 @@ Main repo: https://git.sudoer.ch/me/really-bad-compiler-in-haskell
## Run Instructions ## Run Instructions
- Use `stack run <file>` to run the program (for example, `stack run example/1`). - Use `stack run <file>` to run the program (for example, `stack run example/1.fs`).
- The LLVM will be generated as `a.out.ll`, and the executable will be generated as `a.out`. - The LLVM will be generated as `a.out.ll`, and the executable will be generated as `a.out`.
## Language
It is planned to be similar to Forth, unless it turns out that Forth does not have the features I am looking for.
Currently, it supports positive integers, `+`, `-`, `*`, `/`, and `.` (pops from stack and outputs to console).
The supported syntax for this compiler aims to be compatible with Gforth.
## To edit ## To edit
I recommend using VSCodium, which is preconfigured to have syntax highlighting and (currently broken) debugging features and will automatically suggest the Haskell extensions to install. I recommend using VSCodium, which is preconfigured to have syntax highlighting and (currently broken) debugging features and will automatically suggest the Haskell extensions to install.

View File

@ -1 +0,0 @@
5*3+2

1
example/1.fs 100644
View File

@ -0,0 +1 @@
5 3 * 2 + .

View File

@ -1 +0,0 @@
6+8/3

1
example/2.fs 100644
View File

@ -0,0 +1 @@
6 8 3 / + .

View File

@ -18,7 +18,7 @@ executable really-bad-compiler-in-haskell
other-modules: other-modules:
Forth.LLVMGen Forth.LLVMGen
Forth.Parser Forth.Parser
Forth.Types.Expr Forth.Types.Token
Paths_really_bad_compiler_in_haskell Paths_really_bad_compiler_in_haskell
hs-source-dirs: hs-source-dirs:
src src

View File

@ -7,7 +7,7 @@
module Forth.LLVMGen (llvmGen) where module Forth.LLVMGen (llvmGen) where
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Forth.Types.Expr as Expr import Forth.Types.Token as Token
import LLVM (moduleLLVMAssembly, withModuleFromAST) import LLVM (moduleLLVMAssembly, withModuleFromAST)
import LLVM.AST hiding (function) import LLVM.AST hiding (function)
import LLVM.AST.Type import LLVM.AST.Type
@ -17,7 +17,7 @@ import LLVM.IRBuilder.Instruction
import LLVM.IRBuilder.Module import LLVM.IRBuilder.Module
import LLVM.IRBuilder.Monad import LLVM.IRBuilder.Monad
getLLVM :: Expr -> Module getLLVM :: Token -> Module
getLLVM expr = getLLVM expr =
buildModule "program" $ mdo buildModule "program" $ mdo
-- TODO: better module name -- TODO: better module name
@ -32,22 +32,22 @@ exprToLLVM ::
( MonadIRBuilder m, ( MonadIRBuilder m,
MonadModuleBuilder m MonadModuleBuilder m
) => ) =>
Expr -> Token ->
m Operand m Operand
exprToLLVM (Lit prim) = pure $ primToLLVM prim exprToLLVM (Lit prim) = pure $ primToLLVM prim
exprToLLVM (Expr.Add a b) = mdo exprToLLVM (Token.Add a b) = mdo
lhs <- exprToLLVM a lhs <- exprToLLVM a
rhs <- exprToLLVM b rhs <- exprToLLVM b
add lhs rhs add lhs rhs
exprToLLVM (Expr.Sub a b) = mdo exprToLLVM (Token.Sub a b) = mdo
lhs <- exprToLLVM a lhs <- exprToLLVM a
rhs <- exprToLLVM b rhs <- exprToLLVM b
sub lhs rhs sub lhs rhs
exprToLLVM (Expr.Mul a b) = mdo exprToLLVM (Token.Mul a b) = mdo
lhs <- exprToLLVM a lhs <- exprToLLVM a
rhs <- exprToLLVM b rhs <- exprToLLVM b
mul lhs rhs mul lhs rhs
exprToLLVM (Expr.Div a b) = mdo exprToLLVM (Token.Div a b) = mdo
lhs <- exprToLLVM a lhs <- exprToLLVM a
rhs <- exprToLLVM b rhs <- exprToLLVM b
sdiv lhs rhs sdiv lhs rhs
@ -55,7 +55,7 @@ exprToLLVM (Expr.Div a b) = mdo
primToLLVM :: Int -> Operand primToLLVM :: Int -> Operand
primToLLVM i = int32 (fromIntegral i) primToLLVM i = int32 (fromIntegral i)
llvmGen :: Expr -> IO ByteString llvmGen :: Token -> IO ByteString
llvmGen expr = do llvmGen expr = do
let l = getLLVM expr let l = getLLVM expr
withContext withContext

View File

@ -2,57 +2,53 @@
{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Forth.Parser (parseExpr, ParseResult) where module Forth.Parser (parse) where
import Control.Monad.Combinators.Expr import Data.Text
import Data.Functor.Identity qualified
import Data.Text.Lazy
import Data.Void (Void) import Data.Void (Void)
import Forth.Types.Expr import Forth.Types.Token
import Text.Megaparsec as MP import Text.Megaparsec as MP hiding (Token, parse)
import Text.Megaparsec.Char as C import Text.Megaparsec qualified as MP
import Text.Megaparsec.Char.Lexer as L import Text.Megaparsec.Char qualified as C
import Text.Megaparsec.Char.Lexer qualified as L
import Prelude hiding (div)
type Parser = Parsec Void Text type Parser = Parsec Void Text
lexemeParser :: Parser a -> Parser a lexeme :: Parser a -> Parser a
lexemeParser = L.lexeme C.space lexeme = L.lexeme C.space
symbolParser :: Text -> Parser Text int :: Parser Int
symbolParser = L.symbol C.space int = lexeme $ L.signed (return ()) L.decimal
intParser :: Parser Int symbol :: Text -> Parser Text
intParser = lexemeParser L.decimal symbol = L.symbol C.space
term :: Parser Expr item :: Parser Token
term = Lit <$> intParser item =
try
( Lit
<$> int
)
<|> Add
<$ symbol "+"
<|> Sub
<$ symbol "-"
<|> Mul
<$ symbol "*"
<|> Div
<$ symbol "/"
<|> Pop
<$ symbol "."
table :: [[Operator Parser Expr]] items :: Parser [Token]
table = items = many item
[ [ binaryOp "*" Mul,
binaryOp "/" Div
],
[ binaryOp "+" Add,
binaryOp "-" Sub
-- binaryOp "." Pop
]
]
binaryOp :: Text -> (a -> a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a parseItems :: Text -> Either (ParseErrorBundle Text Void) [Token]
binaryOp name f = InfixL (f <$ symbolParser name) parseItems = MP.parse items ""
-- prefixOp :: Text -> (a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a parse :: Text -> [Token]
-- prefixOp name f = Prefix (f <$ symbolParser name) parse t = do
case parseItems t of
-- postfixOp :: Text -> (a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a Left err -> [] -- putStrLn $ errorBundlePretty err
-- postfixOp name f = Postfix (f <$ symbolParser name) Right tns -> tns
expr :: Parser Expr
expr = makeExprParser term table
type ParseResult = Either (ParseErrorBundle Text Void) Expr
parseExpr :: Text -> ParseResult
parseExpr = MP.parse (C.space *> expr <* eof) ""
-- parseE = parseExpr

View File

@ -0,0 +1,58 @@
-- see https://markkarpov.com/tutorial/megaparsec.html
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
module Forth.Parser (parseExpr, ParseResult) where
import Control.Monad.Combinators.Expr
import Data.Functor.Identity qualified
import Data.Text.Lazy
import Data.Void (Void)
import Forth.Types.Expr
import Text.Megaparsec as MP
import Text.Megaparsec.Char as C
import Text.Megaparsec.Char.Lexer as L
type Parser = Parsec Void Text
lexemeParser :: Parser a -> Parser a
lexemeParser = L.lexeme C.space
symbolParser :: Text -> Parser Text
symbolParser = L.symbol C.space
intParser :: Parser Int
intParser = lexemeParser L.decimal
term :: Parser Expr
term = Lit <$> intParser
table :: [[Operator Parser Expr]]
table =
[ [ binaryOp "*" Mul,
binaryOp "/" Div
],
[ binaryOp "+" Add,
binaryOp "-" Sub
-- binaryOp "." Pop
]
]
binaryOp :: Text -> (a -> a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a
binaryOp name f = InfixL (f <$ symbolParser name)
-- prefixOp :: Text -> (a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a
-- prefixOp name f = Prefix (f <$ symbolParser name)
-- postfixOp :: Text -> (a -> a) -> Operator (ParsecT Void Text Data.Functor.Identity.Identity) a
-- postfixOp name f = Postfix (f <$ symbolParser name)
expr :: Parser Expr
expr = makeExprParser term table
type ParseResult = Either (ParseErrorBundle Text Void) Expr
parseExpr :: Text -> ParseResult
parseExpr = MP.parse (C.space *> expr <* eof) ""
-- parseE = parseExpr

View File

@ -6,7 +6,7 @@ data Expr
| Sub Expr Expr | Sub Expr Expr
| Mul Expr Expr | Mul Expr Expr
| Div Expr Expr | Div Expr Expr
| Pop Expr Expr
deriving deriving
( -- | Pop Expr Expr ( Show
Show
) )

View File

@ -0,0 +1,12 @@
module Forth.Types.Token (Token (..)) where
data Token
= Lit Int
| Add
| Sub
| Mul
| Div
| Pop
deriving
( Show
)

View File

@ -3,27 +3,21 @@
module Main (main) where module Main (main) where
-- import Compiler.ExeGen
import Data.ByteString.Char8 qualified as B import Data.ByteString.Char8 qualified as B
import Data.Text.Lazy.IO qualified as T import Data.Text.IO qualified as T
import Forth.LLVMGen import Forth.LLVMGen
import Forth.Parser import Forth.Parser
import Forth.Types.Expr
import System.Environment import System.Environment
import System.Process import System.Process
getRight :: ParseResult -> Expr
getRight (Right r) = r
main :: IO () main :: IO ()
main = do main = do
fileName <- fmap head getArgs fileName <- fmap head getArgs
contents <- T.readFile fileName contents <- T.readFile fileName
T.putStrLn "- Parsing file..." T.putStrLn "- Parsing file..."
let parsed = getRight (parseExpr contents) let parsed = parse contents
T.putStrLn "- Generating LLVM to './a.out.ll'..." -- T.putStrLn "- Generating LLVM to './a.out.ll'..."
llvmGen parsed >>= B.writeFile "a.out.ll" -- llvmGen parsed >>= B.writeFile "a.out.ll"
T.putStrLn "- Compiling to executable './a.out'..." -- T.putStrLn "- Compiling to executable './a.out'..."
callCommand "clang a.out.ll" -- callCommand "clang a.out.ll"
T.putStrLn "- Done." T.putStrLn "- Done."