Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ Increments:
- [X] Binary Operators on Ints
- [X] Unary Operators on Ints
- [X] Local Variables and Assignment
- [ ] Booleans and Comparisons
- [ ] WriteLn("Hello World") (finally)
- [X] Booleans
- [ ] Branching (if statements)
- [ ] Comparisons
- [ ] WriteLn("Hello World") (finally)
- [ ] Loops
- [ ] Procedures
- [ ] Functions
Expand Down
20 changes: 20 additions & 0 deletions src/HSPC/CodeGen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type OffsetMap = Map.Map String Int32

dataTypeToSize :: HSPCDataType -> Int
dataTypeToSize IntegerType = 64
-- space efficency is clearly a top priority
dataTypeToSize BooleanType = 64

buildOffsetMap :: [(String, HSPCDataType)] -> (Int32, OffsetMap)
buildOffsetMap vars = (totalSize, Map.fromList mappedVars)
Expand Down Expand Up @@ -72,6 +74,9 @@ generateStatement _ ast = error $ "CodeGen for " ++ show ast ++ " not implemente

generateExpression :: OffsetMap -> Expression -> [Word8]
generateExpression _ (IntLiteral i) = [0x48, 0xb8] ++ int64ToLE i -- movabs rax i
generateExpression _ (BoolLiteral b) =
[0x48, 0xb8]
++ (int64ToLE . fromIntegral . fromEnum) b -- movabs rax i
generateExpression offsetMap (Identifier str) =
case Map.lookup str offsetMap of
Just offset -> [0x48, 0x8b, 0x85] ++ int32ToLE offset -- mov rax [rbp - n]
Expand Down Expand Up @@ -99,4 +104,19 @@ generateExpression offsetMap (IntDivide op1 op2) =
++ [0x48, 0x99] -- cqo
++ [0x48, 0xf7, 0x3c, 0x24] -- idiv qword [rsp]
++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space)
generateExpression offsetMap (BoolOr op1 op2) =
generateExpression offsetMap op2
++ [0x50] -- push rax
++ generateExpression offsetMap op1
++ [0x48, 0x0B, 0x04, 0x24] -- or rax, [rsp]
++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space)
generateExpression offsetMap (BoolAnd op1 op2) =
generateExpression offsetMap op2
++ [0x50] -- push rax
++ generateExpression offsetMap op1
++ [0x48, 0x23, 0x04, 0x24] -- and rax, [rsp]
++ [0x48, 0x83, 0xc4, 0x08] -- add rsp 8 (release stack space)
generateExpression offsetMap (BoolNot op) =
generateExpression offsetMap op
++ [0x48, 0x83, 0xf0, 0x01] -- xor rax, 0x1
generateExpression _ ast = error $ "CodeGen for " ++ show ast ++ " not implemented"
148 changes: 103 additions & 45 deletions src/HSPC/Parse.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ module HSPC.Parse
where

import Data.Int (Int64)
import Data.Map qualified as Map
import HSPC.Tokenize (HSPCToken (..))

data Program = Program String [Block]
deriving (Show, Eq)

data HSPCDataType = IntegerType
data HSPCDataType
= IntegerType
| BooleanType
deriving (Show, Eq)

data Block
Expand All @@ -33,6 +36,10 @@ data Statement
data Expression
= Identifier String
| IntLiteral Int64
| BoolLiteral Bool
| BoolNot Expression
| BoolAnd Expression Expression
| BoolOr Expression Expression
| Add Expression Expression
| Subtract Expression Expression
| Multiply Expression Expression
Expand All @@ -49,14 +56,16 @@ data ParseError
| ExpectedVariableDeclaration [HSPCToken]
| ExpectedOperand String
| UnmatchedBracket
| VariableNotDeclared String
| InvalidDataType HSPCToken
| UnexpectedToken HSPCToken
| ExpectedToken HSPCToken
| NotImplemented [HSPCToken]
| NotImplemented String [HSPCToken]
deriving (Show)

toDataType :: HSPCToken -> Either ParseError HSPCDataType
toDataType IntegerTypeTok = Right IntegerType
toDataType BooleanTypeTok = Right BooleanType
toDataType toc = Left $ InvalidDataType toc

parse :: [HSPCToken] -> Either ParseError Program
Expand All @@ -71,10 +80,10 @@ parse _ = Left MustStartWithProgramStatement
parseMainProgramBlock :: [HSPCToken] -> Either ParseError Block
parseMainProgramBlock (VarKeyWordTok : tocs) = do
(vars, rest) <- parseVariableDeclarations [] tocs
(res, _) <- parseStatements [] rest
(res, _) <- parseStatements (Map.fromList vars) [] rest
return $ MainProgramBlock vars res
parseMainProgramBlock (BeginKeyWordTok : tocs) = do
(res, _) <- parseStatements [] tocs
(res, _) <- parseStatements Map.empty [] tocs
return $ MainProgramBlock [] res
parseMainProgramBlock _ = Left InvalidStartOfBlock

Expand All @@ -90,6 +99,7 @@ parseVariableDeclarations acc xs = do
parseVariableDeclarations (expr : acc) rest

parseVariableDeclaration :: [HSPCToken] -> Either ParseError ((String, HSPCDataType), [HSPCToken])
-- TODO: Handle multiple identifiers seperated by commas
parseVariableDeclaration (IdentifierTok name : ColonTok : dataTypeToc : xs) = do
datatype <- toDataType dataTypeToc
rest <- removeLeadingSemiColon xs
Expand Down Expand Up @@ -119,35 +129,43 @@ splitToMatchingBracket toks = go toks [] 0
go (x : xs) acc i = go xs (x : acc) i
go [] _ _ = Left UnmatchedBracket

parseStatements :: [Statement] -> [HSPCToken] -> Either ParseError ([Statement], [HSPCToken])
parseStatements acc (EndKeyWordTok : xs) = Right (reverse acc, xs)
parseStatements _ [] = Left ExpectedEndStatement
parseStatements acc xs = do
(expr, rest) <- parseStatement xs
parseStatements (expr : acc) rest
parseStatements :: Map.Map String HSPCDataType -> [Statement] -> [HSPCToken] -> Either ParseError ([Statement], [HSPCToken])
parseStatements _ acc (EndKeyWordTok : xs) = Right (reverse acc, xs)
parseStatements _ _ [] = Left ExpectedEndStatement
parseStatements vars acc xs = do
(expr, rest) <- parseStatement vars xs
parseStatements vars (expr : acc) rest

parseStatement :: [HSPCToken] -> Either ParseError (Statement, [HSPCToken])
parseStatement (IdentifierTok name : AssignmentTok : xs) = do
parseStatement :: Map.Map String HSPCDataType -> [HSPCToken] -> Either ParseError (Statement, [HSPCToken])
parseStatement varMap (IdentifierTok name : AssignmentTok : xs) = do
let (rhs, rawRest) = break (== SemiColonTok) xs
rest <- removeLeadingSemiColon rawRest
op <- parseIntOperand rhs
op <- case Map.lookup name varMap of
Just dt -> parseOperand dt rhs
Nothing -> Left $ VariableNotDeclared name
return (Assignment name op, rest)
parseStatement
_
(HaltBuiltInTok : OpenBracketTok : CloseBracketTok : SemiColonTok : xs) = Right (Halt (IntLiteral 0), xs)
parseStatement
_
(HaltBuiltInTok : OpenBracketTok : xs) = do
(internal, rawRest) <- splitToMatchingBracket xs
rest <- removeLeadingSemiColon rawRest
op <- parseIntOperand internal
op <- parseOperand IntegerType internal
return (Halt op, rest)
parseStatement tok = Left $ NotImplemented tok
parseStatement _ tok = Left $ NotImplemented "parseStatement" tok

parseOperand :: HSPCDataType -> [HSPCToken] -> Either ParseError Expression
parseOperand IntegerType = parseIntOperand
parseOperand BooleanType = parseBoolOperand

-- IntOperand = Term { ("+" | "-") Term }
-- Term = Factor { ("*" | "div") Factor }
-- Factor = ("+" | "-") Factor | Number | "(" IntOperand ")"
-- Number = // an integer
parseIntOperand :: [HSPCToken] -> Either ParseError Expression
parseIntOperand toks = case breakLastOuter (`elem` [PlusTok, MinusTok]) toks of
parseIntOperand tokens = case breakLastOuter (`elem` [PlusTok, MinusTok]) tokens of
-- starts with +-
([], after) -> parseTerm after
-- doesn't contain +-
Expand All @@ -163,35 +181,75 @@ parseIntOperand toks = case breakLastOuter (`elem` [PlusTok, MinusTok]) toks of
right <- parseTerm after
return $ Subtract left right
_ -> error "Should be unreachable since we break on PlusTok || MinusTok"

parseTerm :: [HSPCToken] -> Either ParseError Expression
parseTerm toks = case breakLastOuter (`elem` [IntDivideTok, MultiplyTok]) toks of
(before, []) -> parseFactor before
(_, [IntDivideTok]) -> Left $ ExpectedOperand "Expected operand after 'div'"
(before, IntDivideTok : after) -> do
left <- parseIntOperand before
right <- parseFactor after
return $ IntDivide left right
(_, [MultiplyTok]) -> Left $ ExpectedOperand "Expected operand after *"
(before, MultiplyTok : after) -> do
left <- parseIntOperand before
right <- parseFactor after
return $ Multiply left right
_ -> error "Should be unreachable since we break on IntDivideTok || MultiplyTok"

parseFactor :: [HSPCToken] -> Either ParseError Expression
parseFactor [LiteralIntTok n] = Right $ IntLiteral n
parseFactor [IdentifierTok name] = Right $ Identifier name
parseFactor (PlusTok : xs) = do
operand <- parseFactor xs
return $ UnaryPlus operand
parseFactor (MinusTok : xs) = do
operand <- parseFactor xs
return $ UnaryMinus operand
parseFactor (OpenBracketTok : xs) = do
where
parseTerm :: [HSPCToken] -> Either ParseError Expression
parseTerm toks = case breakLastOuter (`elem` [IntDivideTok, MultiplyTok]) toks of
(before, []) -> parseFactor before
(_, [IntDivideTok]) -> Left $ ExpectedOperand "Expected operand after 'div'"
(before, IntDivideTok : after) -> do
left <- parseIntOperand before
right <- parseFactor after
return $ IntDivide left right
(_, [MultiplyTok]) -> Left $ ExpectedOperand "Expected operand after *"
(before, MultiplyTok : after) -> do
left <- parseIntOperand before
right <- parseFactor after
return $ Multiply left right
_ -> error "Should be unreachable since we break on IntDivideTok || MultiplyTok"

parseFactor :: [HSPCToken] -> Either ParseError Expression
parseFactor [LiteralIntTok n] = Right $ IntLiteral n
parseFactor [IdentifierTok name] = Right $ Identifier name
parseFactor (PlusTok : xs) = do
operand <- parseFactor xs
return $ UnaryPlus operand
parseFactor (MinusTok : xs) = do
operand <- parseFactor xs
return $ UnaryMinus operand
parseFactor (OpenBracketTok : xs) = do
(inside, remaining) <- splitToMatchingBracket xs
case remaining of
[] -> parseIntOperand inside
(tok : _) -> Left $ UnexpectedToken tok
parseFactor (tok : _) = Left $ UnexpectedToken tok
parseFactor [] = Left $ ExpectedOperand "Expected operand"

-- BoolOperand = BoolTerm { or BoolTerm }
-- BoolTerm = BoolFactor { and BoolFactor }
-- BoolFactor = not BoolFactor | BoolPrimary
-- BoolPrimary = True | False | ( BoolOperand )
parseBoolOperand :: [HSPCToken] -> Either ParseError Expression
parseBoolOperand toks = case breakLastOuter (== OrTok) toks of
(before, []) -> parseBoolTerm before
(_, [OrTok]) -> Left $ ExpectedOperand "Expected operand after 'div'"
(before, OrTok : after) -> do
left <- parseBoolTerm before
right <- parseBoolTerm after
return $ BoolOr left right
_ -> error "Should be unreachable since we break on OrTok"

parseBoolTerm :: [HSPCToken] -> Either ParseError Expression
parseBoolTerm toks = case breakLastOuter (== AndTok) toks of
(before, []) -> parseBoolFactor before
(_, [AndTok]) -> Left $ ExpectedOperand "Expected operand after 'div'"
(before, AndTok : after) -> do
left <- parseBoolFactor before
right <- parseBoolFactor after
return $ BoolAnd left right
_ -> error "Should be unreachable since we break on AndTok"

parseBoolFactor :: [HSPCToken] -> Either ParseError Expression
parseBoolFactor (NotTok : op) = do
opAsExpr <- parseBoolFactor op
return $ BoolNot opAsExpr
parseBoolFactor toks = parseBoolPrimary toks

parseBoolPrimary :: [HSPCToken] -> Either ParseError Expression
parseBoolPrimary [LiteralBoolTok bool] = Right $ BoolLiteral bool
parseBoolPrimary (OpenBracketTok : xs) = do
(inside, remaining) <- splitToMatchingBracket xs
case remaining of
[] -> parseIntOperand inside
[] -> parseBoolOperand inside
(tok : _) -> Left $ UnexpectedToken tok
parseFactor (tok : _) = Left $ UnexpectedToken tok
parseFactor [] = Left $ ExpectedOperand "Expected operand"
parseBoolPrimary (tok : _) = Left $ UnexpectedToken tok
parseBoolPrimary [] = Left $ ExpectedOperand "Expected boolean operand"
11 changes: 11 additions & 0 deletions src/HSPC/Tokenize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import Data.List (isPrefixOf)

data HSPCToken
= LiteralIntTok Int64
| LiteralBoolTok Bool
| ProgramKeyWordTok
| IntegerTypeTok
| BooleanTypeTok
| VarKeyWordTok
| BeginKeyWordTok
| EndKeyWordTok
Expand All @@ -18,6 +20,9 @@ data HSPCToken
| ColonTok
| SemiColonTok
| IntDivideTok
| OrTok
| AndTok
| NotTok
| PlusTok
| MinusTok
| MultiplyTok
Expand Down Expand Up @@ -48,6 +53,12 @@ tokenizeIdentifierOrKeyWord "BEGIN" = BeginKeyWordTok
tokenizeIdentifierOrKeyWord "HALT" = HaltBuiltInTok
tokenizeIdentifierOrKeyWord "DIV" = IntDivideTok
tokenizeIdentifierOrKeyWord "INTEGER" = IntegerTypeTok
tokenizeIdentifierOrKeyWord "BOOLEAN" = BooleanTypeTok
tokenizeIdentifierOrKeyWord "TRUE" = LiteralBoolTok True
tokenizeIdentifierOrKeyWord "FALSE" = LiteralBoolTok False
tokenizeIdentifierOrKeyWord "OR" = OrTok
tokenizeIdentifierOrKeyWord "AND" = AndTok
tokenizeIdentifierOrKeyWord "NOT" = NotTok
tokenizeIdentifierOrKeyWord s = IdentifierTok s

tokenizeOperator :: String -> [HSPCToken]
Expand Down
6 changes: 5 additions & 1 deletion test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ testSuite =
TestSpec "Brackets with literal ints" "operators_brackets" 26,
TestSpec "Unary minus with literal ints" "operators_unary_minus_int" 75,
TestSpec "Unary plus with literal ints" "operators_unary_plus_int" 25,
TestSpec "Or between bools" "operators_bool_or" 0,
TestSpec "And between bools" "operators_bool_and" 0,
TestSpec "Negating bools" "operators_bool_not" 0,
TestSpec "{} comments" "comments_curly_brace" 12,
TestSpec "// comments" "comments_single_line" 12,
TestSpec "Single variable" "variables_single_var" 10
TestSpec "Single variable" "variables_single_var" 10,
TestSpec "Boolean variable" "variables_bool" 0
]

main :: IO ()
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions test/sample_programs/operators_bool_and.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
program BoolAnd;

var
b : boolean;
begin
b := true and false;
halt();
end.
Empty file.
8 changes: 8 additions & 0 deletions test/sample_programs/operators_bool_not.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
program BoolNot;

var
b : boolean;
begin
b := not (true and false);
halt();
end.
Empty file.
8 changes: 8 additions & 0 deletions test/sample_programs/operators_bool_or.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
program BoolOR;

var
b : boolean;
begin
b := true or false;
halt();
end.
Empty file.
8 changes: 8 additions & 0 deletions test/sample_programs/variables_bool.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
program BoolVariable;

var
i : boolean;
begin
i := true;
halt();
end.